{"id":456,"date":"2025-08-27T22:18:31","date_gmt":"2025-08-27T22:18:31","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=456"},"modified":"2025-08-27T22:18:33","modified_gmt":"2025-08-27T22:18:33","slug":"how-to-set-up-laravel-with-caddy-for-performance-https","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-set-up-laravel-with-caddy-for-performance-https\/","title":{"rendered":"How to Set Up Laravel with Caddy for Performance &amp; HTTPS"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>How to Set Up Laravel with Caddy for Performance &amp; HTTPS<\/strong><\/h2>\n\n\n\n<p><strong>Caddy<\/strong> is a modern web server that makes deploying Laravel 12 projects easier and faster. Unlike Nginx or Apache, Caddy has automatic HTTPS via Let\u2019s Encrypt built-in, simple configuration, and excellent support for HTTP\/2 and HTTP\/3 (QUIC). In this step-by-step guide, we\u2019ll set up Laravel behind Caddy, configure PHP-FPM, enable caching &amp; compression, and add best practices for production.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 \u2014 Install Caddy &amp; PHP-FPM<\/strong><\/h2>\n\n\n\n<p>On Ubuntu, you can install Caddy from the official repository and PHP-FPM for Laravel.<\/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\"><span class=\"hljs-comment\"># Install Caddy<\/span>\nsudo apt install -y debian-keyring debian-archive-keyring apt-transport-https\ncurl -1sLf <span class=\"hljs-string\">'https:\/\/dl.cloudsmith.io\/public\/caddy\/stable\/gpg.key'<\/span> | sudo gpg --dearmor -o \/usr\/share\/keyrings\/caddy.gpg\ncurl -1sLf <span class=\"hljs-string\">'https:\/\/dl.cloudsmith.io\/public\/caddy\/stable\/debian.deb.txt'<\/span> | sudo tee \/etc\/apt\/sources.list.d\/caddy-stable.list\nsudo apt update\nsudo apt install caddy -y\n\n<span class=\"hljs-comment\"># Install PHP-FPM 8.3<\/span>\nsudo apt install php8.3-fpm php8.3-cli php8.3-mysql unzip -y<\/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>This installs both Caddy and PHP-FPM. Caddy handles HTTP\/HTTPS while PHP-FPM executes PHP code for Laravel.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 \u2014 Basic Caddyfile for Laravel<\/strong><\/h2>\n\n\n\n<p>Caddy uses a single <code>Caddyfile<\/code> for configuration. Point the root to Laravel\u2019s <code>public\/<\/code> folder, enable PHP, and set caching rules.<\/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-comment\"># \/etc\/caddy\/Caddyfile<\/span>\nyour-domain.<span class=\"hljs-section\">com<\/span> {\n    <span class=\"hljs-attribute\">root<\/span> * \/var\/www\/current\/public\n\n    <span class=\"hljs-comment\"># PHP-FPM<\/span>\n    php_fastcgi unix\/\/run\/php\/php8.<span class=\"hljs-number\">3<\/span>-fpm.sock\n\n    <span class=\"hljs-comment\"># Clean URLs<\/span>\n    try_files {path} {path}\/ \/index.php?{query}\n\n    <span class=\"hljs-comment\"># Static file caching<\/span>\n    @<span class=\"hljs-section\">static<\/span> {\n        <span class=\"hljs-attribute\">path_regexp<\/span> static \\.(?:ico|css|js|gif|jpg|jpeg|png|svg|woff2?)$\n    }\n    header <span class=\"hljs-variable\">@static<\/span> Cache-Control max-age=<span class=\"hljs-number\">604800<\/span>\n\n    <span class=\"hljs-comment\"># Security headers<\/span>\n    header {\n        X-Frame-<span class=\"hljs-attribute\">Options<\/span> SAMEORIGIN\n        X-Content-Type-Options nosniff\n        Referrer-Policy <span class=\"hljs-string\">\"strict-origin-when-cross-origin\"<\/span>\n    }\n\n    encode gzip zstd\n    file_server\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>This simple Caddyfile enables automatic TLS (no extra steps needed for Let\u2019s Encrypt), serves static assets with caching, enables gzip\/zstd compression, and configures PHP-FPM socket handling. Caddy automatically handles certificate renewals.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 \u2014 Zero-Downtime Deploys with Symlinks<\/strong><\/h2>\n\n\n\n<p>Just like Nginx, you can use a <code>current\/<\/code> symlink for zero-downtime deployments. Upload new code to <code>releases\/2025xxxx\/<\/code> and update the symlink.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># Example deployment script<\/span>\n<span class=\"hljs-built_in\">cd<\/span> \/var\/www\/releases\nmkdir 20250828\nunzip \/tmp\/app.zip -d 20250828\nln -sfn \/var\/www\/releases\/20250828 \/var\/www\/current\nphp \/var\/www\/current\/artisan migrate --force\nphp \/var\/www\/current\/artisan config:cache\nphp \/var\/www\/current\/artisan route:cache<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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>The <code>ln -sfn<\/code> command atomically switches the <code>current<\/code> symlink to the new release. No downtime\u2014requests are served by the old code until the new one is ready. For a more advanced and automated approach, check out <a href=\"\/blog\/automating-laravel-deployments-with-deployer\">Automating Laravel Deployments with Deployer<\/a> .<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 \u2014 Handling Queues &amp; Scheduler<\/strong><\/h2>\n\n\n\n<p>Shared hosting often won\u2019t give you Supervisor. With Caddy on VPS, you can configure Supervisor for workers and cron for scheduled tasks.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># \/etc\/supervisor\/conf.d\/laravel-worker.conf<\/span>\n&#91;program:laravel-worker]\nprocess_name=%(program_name)s_%(process_num)02d\n<span class=\"hljs-built_in\">command<\/span>=php \/var\/www\/current\/artisan queue:work --sleep=3 --tries=3 --max-time=3600\nautostart=<span class=\"hljs-literal\">true<\/span>\nautorestart=<span class=\"hljs-literal\">true<\/span>\nnumprocs=3\nredirect_stderr=<span class=\"hljs-literal\">true<\/span>\nstdout_logfile=\/var\/www\/current\/storage\/logs\/worker.log<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><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>This keeps 3 queue workers always alive. Pair with Laravel Horizon  for UI monitoring. For scheduled tasks, add a cron entry to call <code>artisan schedule:run<\/code> every minute.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 \u2014 UI: TLS &amp; Header Checker<\/strong><\/h2>\n\n\n\n<p>Caddy\u2019s automatic TLS is great, but you might want a simple Laravel admin page to confirm HTTPS and headers are applied correctly.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" 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::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'can:viewAdmin'<\/span>])-&gt;get(<span class=\"hljs-string\">'\/caddy-status'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'admin.caddy-status'<\/span>, &#91;\n        <span class=\"hljs-string\">'scheme'<\/span> =&gt; request()-&gt;getScheme(),\n        <span class=\"hljs-string\">'headers'<\/span> =&gt; &#91;\n            <span class=\"hljs-string\">'X-Frame-Options'<\/span> =&gt; request()-&gt;header(<span class=\"hljs-string\">'X-Frame-Options'<\/span>),\n            <span class=\"hljs-string\">'X-Content-Type-Options'<\/span> =&gt; request()-&gt;header(<span class=\"hljs-string\">'X-Content-Type-Options'<\/span>),\n            <span class=\"hljs-string\">'Referrer-Policy'<\/span> =&gt; request()-&gt;header(<span class=\"hljs-string\">'Referrer-Policy'<\/span>),\n            <span class=\"hljs-string\">'Content-Encoding'<\/span> =&gt; request()-&gt;header(<span class=\"hljs-string\">'Content-Encoding'<\/span>),\n        ]\n    ]);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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 route passes key header and scheme info to a Blade view for inspection. You\u2019ll see if the request is truly over HTTPS and whether compression headers (gzip\/zstd) are active.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-comment\">&lt;!-- resources\/views\/admin\/caddy-status.blade.php --&gt;<\/span>\n@extends('layouts.app')\n@section('content')\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mb-4\"<\/span>&gt;<\/span>Caddy Status<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>Scheme:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> {{ $scheme }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"list-group\"<\/span>&gt;<\/span>\n    @foreach($headers as $key =&gt; $value)\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"list-group-item\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>{{ $key }}:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> {{ $value ?? '\u2014' }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n    @endforeach\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n@endsection<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This UI confirms your Caddy TLS and security headers are correctly applied, helping you quickly verify production readiness.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Caddy is an excellent alternative to Nginx for Laravel deployments: it\u2019s easy to configure, has automatic HTTPS, and supports HTTP\/2 and HTTP\/3 out of the box. By combining Caddy with PHP-FPM, caching, compression, Supervisor for queues, and good deployment practices, you\u2019ll have a modern, fast, and secure Laravel production setup.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/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\/laravel-nginx-best-practices-for-production\">Laravel &amp; Nginx: Best Practices for Production<\/a> \u2014 compare Nginx vs Caddy approaches.<\/li>\n\n\n\n<li><a href=\"\/blog\/optimizing-laravel-for-aws-deployment-step-by-step\">Optimizing Laravel for AWS Deployment (Step-by-Step)<\/a> \u2014 learn how to scale containers and servers.<\/li>\n\n\n\n<li><a href=\"\/blog\/laravel-deployment-checklist-for-2025\">Laravel Deployment Checklist for 2025<\/a> \u2014 don\u2019t go live without reviewing this list.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>How to Set Up Laravel with Caddy for Performance &amp; HTTPS Caddy is a modern web server that makes deploying Laravel 12 projects easier and faster. Unlike Nginx or Apache, Caddy has automatic HTTPS via Let\u2019s Encrypt built-in, simple configuration, and excellent support for HTTP\/2 and HTTP\/3 (QUIC). In this step-by-step guide, we\u2019ll set up [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":460,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[83,74,71],"class_list":["post-456","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-caddy","tag-deployment","tag-devops"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/456","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=456"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/456\/revisions"}],"predecessor-version":[{"id":459,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/456\/revisions\/459"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/460"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}