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):
- Go to App > Settings > Domains
- Select your domain and click "Custom SSL Certificate"
- Paste your certificate chain and private key
- 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.