Fixing Nginx “Permission denied” Error for client_body Temp Directory in Node.js File Upload Setup


2 views

When working with file uploads through Nginx proxy to a Node.js backend, a common but frustrating error occurs:

2015/10/09 15:08:44 [crit] 1231#0: *5 open() "/var/lib/nginx/client_body/0000000003" failed (13: Permission denied)

The error emerges because Nginx needs to store temporary files during file uploads before passing them to your Node.js application. The default directory (/var/lib/nginx/client_body) either:

  • Doesn't have proper permissions for the Nginx worker process
  • Doesn't exist in your filesystem
  • Has incorrect SELinux/AppArmor policies (if enabled)

Here's how to permanently resolve this across different Linux distributions:

1. Verify and Fix Directory Permissions

sudo mkdir -p /var/lib/nginx/client_body
sudo chown -R nginx:nginx /var/lib/nginx
sudo chmod -R 770 /var/lib/nginx

2. Alternative: Configure Custom Temp Directory

In your Nginx configuration (/etc/nginx/nginx.conf):

http {
    client_body_temp_path /tmp/nginx_upload 1 2;
    # ... other configurations
}

Then set permissions:

sudo mkdir /tmp/nginx_upload
sudo chown nginx:nginx /tmp/nginx_upload
sudo chmod 700 /tmp/nginx_upload

For RHEL/CentOS/Fedora:

sudo chcon -Rt httpd_sys_content_t /var/lib/nginx/client_body
# OR temporarily allow
sudo setenforce 0

Ensure your Express/multer configuration matches the Nginx settings:

const express = require('express');
const multer  = require('multer');
const upload = multer({ dest: '/tmp/node_uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
    // Handle file
});
  1. Restart Nginx: sudo systemctl restart nginx
  2. Check process owner: ps aux | grep nginx
  3. Test upload functionality
  4. Monitor logs: tail -f /var/log/nginx/error.log
  • For high-traffic systems, place temp directory on a separate partition
  • Implement proper cleanup routines for temp files
  • Consider using direct uploads to S3/MinIO for large files

When running Node.js with Nginx as a reverse proxy on Linux systems (particularly Slackware in this case), you might encounter this critical error in your Nginx error logs:

2023/10/09 15:08:44 [crit] 1231#0: *5 open() "/var/lib/nginx/client_body/0000000003" 
failed (13: Permission denied), client: 10.0.0.22, server: localhost, 
request: "POST /upload HTTP/1.1", host: "example.com"

This occurs when Nginx tries to buffer client request bodies (like file uploads) to temporary files but lacks proper permissions to write to its client_body temporary directory. The issue manifests differently across Linux distributions due to varying default permission setups.

1. Verify and Correct Directory Permissions

First check the ownership and permissions of Nginx's client body temp directory:

ls -ld /var/lib/nginx/client_body
# Typical output showing incorrect permissions:
# drwx------ 2 root root 4096 Oct 9 15:00 /var/lib/nginx/client_body

To fix:

sudo chown -R nginx:nginx /var/lib/nginx
sudo chmod -R 770 /var/lib/nginx

2. Configure Alternative Temp Directory

In your nginx.conf, specify a different directory with proper permissions:

http {
    client_body_temp_path /tmp/nginx_client_body 1 2;
    # ...
}

Then create and set permissions:

sudo mkdir -p /tmp/nginx_client_body
sudo chown nginx:nginx /tmp/nginx_client_body
sudo chmod 700 /tmp/nginx_client_body

3. Adjust Nginx Worker Process User

Ensure your nginx worker processes run as the correct user. In nginx.conf:

user nginx;
worker_processes auto;

When handling file uploads in Node.js with Nginx, consider these additional configurations:

const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();

// Middleware configuration
app.use(fileUpload({
    limits: { fileSize: 50 * 1024 * 1024 }, // 50MB limit
    useTempFiles: true,
    tempFileDir: '/tmp/nodejs-uploads/'
}));

// Upload endpoint
app.post('/upload', (req, res) => {
    if (!req.files || Object.keys(req.files).length === 0) {
        return res.status(400).send('No files were uploaded.');
    }
    
    const uploadedFile = req.files.file;
    const uploadPath = __dirname + '/uploads/' + uploadedFile.name;

    uploadedFile.mv(uploadPath, (err) => {
        if (err) return res.status(500).send(err);
        res.send('File uploaded!');
    });
});

Ensure your Nginx proxy configuration includes these critical directives:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # Critical for file uploads
        client_max_body_size 100M;
        proxy_request_buffering off;
        proxy_buffering off;
    }
}

If using SELinux (common on CentOS/RHEL), you may need to adjust security contexts:

# Check current context
ls -Z /var/lib/nginx/client_body

# Apply correct context
sudo chcon -Rt httpd_sys_content_t /var/lib/nginx/client_body
sudo semanage fcontext -a -t httpd_sys_content_t "/var/lib/nginx/client_body(/.*)?"
sudo restorecon -Rv /var/lib/nginx

For development environments, you can disable client body buffering entirely (not recommended for production):

http {
    client_body_in_file_only off;
    client_body_buffer_size 0;
}