CloudPloy

Nginx on CloudPloy

CloudPloy uses Nginx as the front-facing web server for all applications. Nginx handles SSL termination, HTTP/2, static file serving, request routing to your application container (via PHP-FPM or reverse proxy), gzip compression, and security headers. This guide explains how CloudPloy configures Nginx and what you can customize.

How Nginx Fits Into the Stack

The request flow for a CloudPloy application:

Browser -> Cloudflare CDN -> CloudPloy Nginx -> Application Container

For PHP apps (WordPress, Laravel, Symfony):

Nginx -> PHP-FPM (via Unix socket or TCP port)

For Node.js apps:

Nginx -> Node.js HTTP server (via localhost:PORT)

Nginx sits on port 80 and 443 on the host. Your application container exposes a port internally. Nginx proxies external requests to the container while adding SSL, compression, and caching headers.

Default Nginx Configuration

CloudPloy generates an Nginx configuration block for each application. The defaults provide a solid production baseline:

Feature Default Setting
SSL Let's Encrypt certificate, auto-renewed
HTTP/2 Enabled on HTTPS connections
HTTP to HTTPS redirect 301 redirect from port 80 to 443
Gzip compression Enabled for text, CSS, JS, JSON, XML
Client body size 32MB (covers most file uploads)
Proxy read timeout 60 seconds
Static file cache 1 year for .css, .js, .png, .jpg, .woff2
Security headers X-Frame-Options, X-XSS-Protection, X-Content-Type-Options

PHP-FPM Integration

For PHP applications, Nginx passes requests to PHP-FPM using the FastCGI protocol. CloudPloy handles this configuration automatically. The effective configuration looks like:

server {
    listen 443 ssl http2;
    server_name yourapp.com;
    root /var/www/html/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

For WordPress, the configuration includes additional rules to block access to sensitive files and handle WordPress permalink rewriting:

    # Block access to sensitive WordPress files
    location ~* /(wp-config\.php|readme\.html|license\.txt) {
        deny all;
    }

    # WordPress uploads - block PHP execution in uploads directory
    location ~* /uploads/.*\.php$ {
        deny all;
    }

Custom Nginx Configuration

Add custom Nginx directives in App > Settings > Nginx Configuration. Directives go inside the server {} block for your application. Useful customizations:

Increase Upload Size

client_max_body_size 128m;

Increase Proxy Timeout for Long-Running Requests

proxy_read_timeout 300s;
fastcgi_read_timeout 300s;

Add CORS Headers for an API

add_header 'Access-Control-Allow-Origin' '$http_origin' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;

if ($request_method = OPTIONS) {
    return 204;
}

Add Strict Transport Security (HSTS)

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Block Direct IP Access

# Reject requests without a recognized Host header
if ($host !~* "^(yourapp\.com|www\.yourapp\.com)$") {
    return 444;
}

Rate Limiting

Protect login endpoints and APIs from brute force attacks with Nginx rate limiting. Configure in App > Settings > Nginx Configuration:

# Limit login attempts: 5 requests per minute per IP
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

location ~* /(wp-login\.php|login|api/auth) {
    limit_req zone=login burst=10 nodelay;
    limit_req_status 429;
    # Pass to PHP/app after rate limit check
    try_files $uri $uri/ /index.php?$query_string;
}

WebSocket Proxying

WebSocket connections require specific Nginx proxy settings. CloudPloy includes these by default for Node.js applications. If you need to configure it manually:

location /ws/ {
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 86400s;  # 24 hours - keep WS connections alive
}

Reverse Proxy for Multiple Services

If your application runs multiple services (for example, a main API on port 3000 and a metrics service on port 9090), configure upstream blocks:

upstream api {
    server localhost:3000;
    keepalive 32;
}

upstream metrics {
    server localhost:9090;
}

location /api/ {
    proxy_pass http://api;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

location /metrics {
    proxy_pass http://metrics;
    allow 10.0.0.0/8;   # Only allow internal network
    deny all;
}

Gzip Compression

CloudPloy enables gzip compression by default. For even better compression ratios, Brotli is available where the browser supports it:

gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
    application/javascript
    application/json
    application/xml
    text/css
    text/html
    text/plain
    image/svg+xml
    font/woff2;

SSL Certificate Management

CloudPloy automatically provisions Let's Encrypt SSL certificates when you attach a domain. The certificate auto-renews 30 days before expiration with no manual action required.

For custom SSL certificates (EV certificates, wildcard certificates from a commercial CA):

  1. Go to App > Settings > Domains
  2. Select your domain and click "Custom SSL Certificate"
  3. Paste your certificate chain and private key
  4. CloudPloy updates Nginx configuration automatically

Verify your SSL configuration after setup:

# Test SSL from App > Terminal
curl -I https://yourapp.com

# Check certificate expiry
echo | openssl s_client -connect yourapp.com:443 2>/dev/null | openssl x509 -noout -dates

Static File Optimization

For static files (CSS, JavaScript, images, fonts), Nginx serves them directly without involving PHP or Node.js. Ensure your application's public directory is correctly mapped:

# Long-lived cache for fingerprinted assets (Vite, Webpack output)
location ~* \.(js|css)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Moderate cache for images (may change without filename change)
location ~* \.(png|jpg|jpeg|gif|ico|svg|webp)$ {
    expires 30d;
    add_header Cache-Control "public";
}

# Font files - long cache + CORS for cross-origin use
location ~* \.(woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Access-Control-Allow-Origin "*";
}

Debugging Nginx Issues

Check Nginx Error Logs

Nginx errors appear in App > Logs alongside your application logs. Look for lines starting with [error] or [warn].

Common Error Codes

Error Code Meaning Common Cause
502 Bad Gateway Nginx cannot reach the upstream app App crashed, wrong port, PHP-FPM not running
504 Gateway Timeout App took too long to respond Slow query, long computation, proxy_read_timeout too short
413 Request Entity Too Large Upload exceeds client_max_body_size Increase client_max_body_size in custom Nginx config
444 Connection closed with no response Intentional - configured to block bad Host headers
403 Forbidden Access denied by Nginx rules IP blocking, deny rule matched, wrong file permissions

Test Nginx Configuration

From App > Terminal:

# Test Nginx config syntax
nginx -t

# Reload Nginx after config change (CloudPloy does this automatically)
nginx -s reload

# Check which Nginx config is active
nginx -T | head -50

Nginx Guides

  • SSL Certificate Setup - Attach a domain, provision a Let's Encrypt certificate, configure HTTPS redirect, and set up HSTS for production security.

Need a custom Nginx configuration not covered here? Add it in App > Settings > Nginx Configuration, or contact CloudPloy support for configurations that require server-level changes.