Перейти к содержанию

Laravel

Production-ready NGINX configuration for Laravel 10/11 applications.


Basic Configuration

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    http2 on;
    listen [::]:443 ssl;
    server_name example.com www.example.com;

    root /var/www/laravel/public;
    index index.php;

    # SSL
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Logging
    access_log /var/log/nginx/laravel.access.log;
    error_log /var/log/nginx/laravel.error.log;

    # Max upload size
    client_max_body_size 100m;

    # Deny hidden files (except .well-known)
    location ~ /\.(?!well-known) {
        deny all;
    }

    # Block access to sensitive files
    location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ {
        deny all;
    }

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

    # PHP handling
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
        fastcgi_param HTTP_PROXY "";
        fastcgi_read_timeout 300;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    }

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

    # Deny access to .env file
    location ~ /\.env {
        deny all;
    }
}

Laravel with Octane (Swoole/RoadRunner)

For high-performance Laravel Octane:

upstream octane {
    server 127.0.0.1:8000;
    keepalive 32;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    root /var/www/laravel/public;

    # SSL config...

    # Static files served directly
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Everything else to Octane
    location / {
        proxy_pass http://octane;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_read_timeout 300;
    }
}

Start Octane:

php artisan octane:start --server=swoole --host=127.0.0.1 --port=8000

Laravel WebSockets / Reverb

For Laravel Reverb or WebSockets:

upstream websocket {
    server 127.0.0.1:8080;
}

server {
    listen 443 ssl;
    http2 on;
    server_name example.com;

    # ... base config ...

    # WebSocket endpoint
    location /app {
        proxy_pass http://websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400;
    }

    # Broadcasting auth
    location /broadcasting/auth {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

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

    location ~ \.php$ {
        # PHP-FPM config...
    }
}

Laravel Horizon (Queue Dashboard)

Protect Horizon dashboard:

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

    # Optional: IP restriction
    # allow 10.0.0.0/8;
    # deny all;
}

API Rate Limiting

# Define rate limit zones
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

server {
    # ... base config ...

    # API endpoints
    location /api {
        limit_req zone=api burst=20 nodelay;
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Login rate limiting
    location = /login {
        limit_req zone=login burst=3 nodelay;
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        # PHP-FPM config...
    }
}

FastCGI Caching

# Cache zone (http block)
fastcgi_cache_path /var/cache/nginx/laravel
    levels=1:2
    keys_zone=laravel:100m
    inactive=60m
    max_size=1g;

server {
    set $skip_cache 0;

    # Don't cache authenticated requests
    if ($http_authorization != "") { set $skip_cache 1; }
    if ($http_cookie ~* "laravel_session") { set $skip_cache 1; }

    # Don't cache specific paths
    if ($request_uri ~* "^/(api|nova|horizon|telescope)") { set $skip_cache 1; }
    if ($request_method = POST) { set $skip_cache 1; }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;

        # Caching
        fastcgi_cache laravel;
        fastcgi_cache_valid 200 10m;
        fastcgi_cache_bypass $skip_cache;
        fastcgi_no_cache $skip_cache;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

Laravel Configuration

Trusted Proxies

In app/Http/Middleware/TrustProxies.php:

protected $proxies = '*';

protected $headers =
    Request::HEADER_X_FORWARDED_FOR |
    Request::HEADER_X_FORWARDED_HOST |
    Request::HEADER_X_FORWARDED_PORT |
    Request::HEADER_X_FORWARDED_PROTO;

.env Settings

APP_URL=https://example.com
ASSET_URL=https://example.com

# For Octane
OCTANE_SERVER=swoole

# Session/Cookie security
SESSION_SECURE_COOKIE=true

Deployment Checklist

# Optimize for production
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

# Clear caches when deploying
php artisan optimize:clear
php artisan optimize

Common Issues

Issue Solution
404 on all routes Check try_files ends with $query_string
CSRF token mismatch Ensure APP_URL matches actual URL
Mixed content Set ASSET_URL to HTTPS URL
File uploads fail Increase client_max_body_size and PHP limits
Session issues Check cookie secure settings match HTTPS

See Also