{"id":451,"date":"2025-08-27T22:10:26","date_gmt":"2025-08-27T22:10:26","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=451"},"modified":"2025-08-27T22:28:31","modified_gmt":"2025-08-27T22:28:31","slug":"laravel-nginx-best-practices-for-production","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/laravel-nginx-best-practices-for-production\/","title":{"rendered":"Laravel &#038; Nginx: Best Practices for Production"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Laravel &amp; Nginx: Best Practices for Production<\/strong><\/h2>\n\n\n\n<p><strong>Caddy<\/strong> might be the new kid on the block, but <strong>Nginx<\/strong> remains the most widely used web server for Laravel in production. It\u2019s stable, efficient, and battle-tested at scale. To run a Laravel 12 application smoothly in production with Nginx, you\u2019ll need the right configuration, caching rules, and security headers. In this guide, we\u2019ll walk through best practices to deploy Laravel with Nginx.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 \u2014 Install Nginx &amp; PHP-FPM<\/strong><\/h2>\n\n\n\n<p>Most Linux servers don\u2019t ship with Nginx preinstalled. You\u2019ll need Nginx plus PHP-FPM to handle dynamic PHP requests efficiently.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">sudo apt update\nsudo apt install -y nginx php8.3-fpm php8.3-mysql unzip curl<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We install the <code>nginx<\/code> server, PHP 8.3 FPM for executing Laravel, and supporting packages. If you\u2019re deploying on a provider like DigitalOcean or AWS, see <a href=\"\/blog\/how-to-deploy-a-laravel-12-app-on-digitalocean\">How to Deploy a Laravel 12 App on DigitalOcean<\/a> and <a href=\"\/blog\/deploying-laravel-on-aws-complete-guide-2025\">Deploying Laravel on AWS: Complete Guide (2025)<\/a> for cloud-specific steps.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 \u2014 Nginx Server Block for Laravel<\/strong><\/h2>\n\n\n\n<p>Create a new config file <code>\/etc\/nginx\/sites-available\/laravel.conf<\/code> and point it to the <code>public\/<\/code> directory. This ensures only public assets are directly accessible.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Nginx\" data-shcb-language-slug=\"nginx\"><span><code class=\"hljs language-nginx\"><span class=\"hljs-section\">server<\/span> {\n    <span class=\"hljs-attribute\">listen<\/span> <span class=\"hljs-number\">80<\/span>;\n    <span class=\"hljs-attribute\">server_name<\/span> your-domain.com;\n    <span class=\"hljs-attribute\">root<\/span> \/var\/www\/current\/public;\n\n    <span class=\"hljs-attribute\">index<\/span> index.php index.html;\n\n    <span class=\"hljs-attribute\">location<\/span> \/ {\n        <span class=\"hljs-attribute\">try_files<\/span> <span class=\"hljs-variable\">$uri<\/span> <span class=\"hljs-variable\">$uri<\/span>\/ \/index.php?<span class=\"hljs-variable\">$query_string<\/span>;\n    }\n\n    <span class=\"hljs-attribute\">location<\/span> <span class=\"hljs-regexp\">~ \\.php$<\/span> {\n        <span class=\"hljs-attribute\">include<\/span> fastcgi_params;\n        <span class=\"hljs-attribute\">fastcgi_pass<\/span> unix:\/var\/run\/php\/php8.3-fpm.sock;\n        <span class=\"hljs-attribute\">fastcgi_param<\/span> SCRIPT_FILENAME <span class=\"hljs-variable\">$realpath_root<\/span><span class=\"hljs-variable\">$fastcgi_script_name<\/span>;\n        <span class=\"hljs-attribute\">include<\/span> fastcgi_params;\n    }\n\n    <span class=\"hljs-attribute\">location<\/span> <span class=\"hljs-regexp\">~ \/\\.ht<\/span> {\n        <span class=\"hljs-attribute\">deny<\/span> all;\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Nginx<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">nginx<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here, <code>root<\/code> points to <code>public\/<\/code>, and all requests fall back to <code>index.php<\/code> if no file is found. The <code>fastcgi_pass<\/code> points to the PHP-FPM socket (adjust version accordingly).<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 \u2014 Enable Compression<\/strong><\/h2>\n\n\n\n<p>Gzip (or Brotli if supported) should be enabled to reduce file sizes. Add this to your <code>nginx.conf<\/code> under the <code>http<\/code> block.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Nginx\" data-shcb-language-slug=\"nginx\"><span><code class=\"hljs language-nginx\"><span class=\"hljs-attribute\">gzip<\/span> <span class=\"hljs-literal\">on<\/span>;\n<span class=\"hljs-attribute\">gzip_comp_level<\/span> <span class=\"hljs-number\">5<\/span>;\n<span class=\"hljs-attribute\">gzip_min_length<\/span> <span class=\"hljs-number\">1024<\/span>;\n<span class=\"hljs-attribute\">gzip_proxied<\/span> any;\n<span class=\"hljs-attribute\">gzip_types<\/span> text\/plain text\/css application\/javascript application\/json application\/xml image\/svg+xml;\n<span class=\"hljs-attribute\">gzip_vary<\/span> <span class=\"hljs-literal\">on<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Nginx<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">nginx<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This ensures text-based assets are compressed before sending to the client, saving bandwidth and improving speed. For further performance tuning, check <a href=\"\/blog\/10-proven-ways-to-optimize-laravel-for-high-traffic\">10 Proven Ways to Optimize Laravel for High Traffic<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 \u2014 Handling HTTPS<\/strong><\/h2>\n\n\n\n<p>Use Let\u2019s Encrypt certificates or integrate with your hosting provider\u2019s SSL. With Nginx, configure your <code>server<\/code> block for HTTPS:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Nginx\" data-shcb-language-slug=\"nginx\"><span><code class=\"hljs language-nginx\"><span class=\"hljs-section\">server<\/span> {\n    <span class=\"hljs-attribute\">listen<\/span> <span class=\"hljs-number\">443<\/span> ssl http2;\n    <span class=\"hljs-attribute\">server_name<\/span> your-domain.com;\n\n    <span class=\"hljs-attribute\">ssl_certificate<\/span> \/etc\/letsencrypt\/live\/your-domain.com\/fullchain.pem;\n    <span class=\"hljs-attribute\">ssl_certificate_key<\/span> \/etc\/letsencrypt\/live\/your-domain.com\/privkey.pem;\n\n    <span class=\"hljs-attribute\">include<\/span> snippets\/ssl-params.conf;\n\n    <span class=\"hljs-attribute\">root<\/span> \/var\/www\/current\/public;\n    <span class=\"hljs-attribute\">index<\/span> index.php index.html;\n\n    <span class=\"hljs-attribute\">location<\/span> \/ {\n        <span class=\"hljs-attribute\">try_files<\/span> <span class=\"hljs-variable\">$uri<\/span> <span class=\"hljs-variable\">$uri<\/span>\/ \/index.php?<span class=\"hljs-variable\">$query_string<\/span>;\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Nginx<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">nginx<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With this, Caddy would have automated TLS by default, but in Nginx you typically configure Let\u2019s Encrypt with <code>certbot<\/code>. If you\u2019re deploying on a cloud provider like AWS, offload TLS termination to an ALB or load balancer (<a href=\"\/blog\/deploying-laravel-on-aws-complete-guide-2025\">Deploying Laravel on AWS: Complete Guide (2025)<\/a>).<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 \u2014 Optimize Nginx &amp; PHP-FPM<\/strong><\/h2>\n\n\n\n<p>Set proper buffer and worker configurations in <code>nginx.conf<\/code> and tune PHP-FPM for concurrent requests.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Nginx\" data-shcb-language-slug=\"nginx\"><span><code class=\"hljs language-nginx\"><span class=\"hljs-comment\"># \/etc\/nginx\/nginx.conf (http { ... })<\/span>\n<span class=\"hljs-attribute\">worker_processes<\/span> auto;\n<span class=\"hljs-attribute\">worker_connections<\/span> <span class=\"hljs-number\">1024<\/span>;\n<span class=\"hljs-attribute\">client_max_body_size<\/span> <span class=\"hljs-number\">20M<\/span>;\n\n<span class=\"hljs-comment\"># PHP upstream for TCP (if not using socket)<\/span>\n<span class=\"hljs-attribute\">upstream<\/span> php {\n    <span class=\"hljs-attribute\">server<\/span> <span class=\"hljs-number\">127.0.0.1:9000<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Nginx<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">nginx<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here, <code>worker_processes auto;<\/code> lets Nginx use available CPU cores. The <code>client_max_body_size<\/code> ensures large uploads don\u2019t get rejected prematurely.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"TOML, also INI\" data-shcb-language-slug=\"ini\"><span><code class=\"hljs language-ini\"><span class=\"hljs-comment\">; \/etc\/php\/8.3\/fpm\/pool.d\/www.conf<\/span>\n<span class=\"hljs-attr\">pm<\/span> = dynamic\n<span class=\"hljs-attr\">pm.max_children<\/span> = <span class=\"hljs-number\">20<\/span>\n<span class=\"hljs-attr\">pm.start_servers<\/span> = <span class=\"hljs-number\">4<\/span>\n<span class=\"hljs-attr\">pm.min_spare_servers<\/span> = <span class=\"hljs-number\">4<\/span>\n<span class=\"hljs-attr\">pm.max_spare_servers<\/span> = <span class=\"hljs-number\">8<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">TOML, also INI<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ini<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>These PHP-FPM settings control concurrency. Adjust <code>pm.max_children<\/code> based on available memory and expected traffic. For very high traffic, consider <a href=\"\/blog\/optimizing-laravel-for-high-concurrency-with-octane\">Optimizing Laravel for High Concurrency with Octane<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 \u2014 Add a Health Check Route<\/strong><\/h2>\n\n\n\n<p>Monitoring your Laravel + Nginx deployment ensures uptime. Add a simple route for your load balancer or uptime monitor.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\nRoute::get(<span class=\"hljs-string\">'\/health'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> response()-&gt;json(&#91;\n        <span class=\"hljs-string\">'status'<\/span> =&gt; <span class=\"hljs-string\">'ok'<\/span>,\n        <span class=\"hljs-string\">'app'<\/span> =&gt; config(<span class=\"hljs-string\">'app.name'<\/span>),\n        <span class=\"hljs-string\">'time'<\/span> =&gt; now(),\n    ]);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This <code>\/health<\/code> endpoint is lightweight and lets Nginx or a monitoring tool confirm your Laravel app is alive. You can extend it with database or cache checks for better observability.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Deploying Laravel 12 with Nginx is reliable and production-friendly when configured correctly. Focus on secure server blocks, efficient PHP-FPM tuning, caching, and TLS. For large-scale scenarios, you may want to explore <a href=\"\/blog\/optimizing-laravel-for-high-concurrency-with-octane\">Optimizing Laravel for High Concurrency with Octane<\/a>. If you\u2019re still on shared hosting, revisit <a href=\"\/blog\/how-to-deploy-laravel-12-app-to-shared-hosting\">How to Deploy Laravel to Shared Hosting (Step by Step)<\/a> to compare approaches.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/step-by-step-cicd-pipeline-setup-for-laravel-12-on-github-actions\">Step-by-Step CI\/CD Pipeline Setup for Laravel 12 on GitHub Actions<\/a> \u2014 automate deployments with testing &amp; caching.<\/li>\n<li><a href=\"\/blog\/laravel-deployment-checklist-for-2025\">Laravel Deployment Checklist for 2025<\/a> \u2014 essential production readiness checklist.<\/li>\n<li><a href=\"\/blog\/optimizing-laravel-for-aws-deployment-step-by-step\">Optimizing Laravel for AWS Deployment (Step-by-Step)<\/a> \u2014 integrate Nginx with AWS services.<\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Laravel &amp; Nginx: Best Practices for Production Caddy might be the new kid on the block, but Nginx remains the most widely used web server for Laravel in production. It\u2019s stable, efficient, and battle-tested at scale. To run a Laravel 12 application smoothly in production with Nginx, you\u2019ll need the right configuration, caching rules, and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":464,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[71,82,44],"class_list":["post-451","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-devops","tag-nginx","tag-performance"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=451"}],"version-history":[{"count":2,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/451\/revisions"}],"predecessor-version":[{"id":465,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/451\/revisions\/465"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/464"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}