{"id":1677,"date":"2025-11-10T23:57:58","date_gmt":"2025-11-10T20:57:58","guid":{"rendered":"https:\/\/www.dchost.com\/blog\/how-i-keep-websockets-and-grpc-happy-behind-cloudflare-the-calm-guide-to-nginx-timeouts-keep%e2%80%91alive-and-zero%e2%80%91downtime\/"},"modified":"2025-11-10T23:57:58","modified_gmt":"2025-11-10T20:57:58","slug":"how-i-keep-websockets-and-grpc-happy-behind-cloudflare-the-calm-guide-to-nginx-timeouts-keep%e2%80%91alive-and-zero%e2%80%91downtime","status":"publish","type":"post","link":"https:\/\/www.dchost.com\/blog\/en\/how-i-keep-websockets-and-grpc-happy-behind-cloudflare-the-calm-guide-to-nginx-timeouts-keep%e2%80%91alive-and-zero%e2%80%91downtime\/","title":{"rendered":"How I Keep WebSockets and gRPC Happy Behind Cloudflare: The Calm Guide to Nginx Timeouts, Keep\u2011Alive, and Zero\u2011Downtime"},"content":{"rendered":"<div class=\"dchost-blog-content-wrapper\"><p>I still remember the first time a client pinged me late on a Friday: \u201cOur dashboards freeze every few minutes\u2026 and the mobile app drops calls randomly.\u201d The stack looked clean at first glance\u2014Cloudflare in front, Nginx on the origin, a mix of WebSocket streams and a shiny new gRPC backend. But buried in the quiet corners of the config were tiny timeout defaults and missing keep\u2011alives, and those little gremlins were enough to make a rock\u2011solid app feel unreliable. Once we untangled the chain\u2014edge, origin, upstream\u2014and gave each leg the right timeouts, headers, and graceful reloads, the system just\u2026 exhaled.<\/p>\n<p>If you\u2019ve felt that same \u201cwhy does this drop under light load?\u201d frustration, this one\u2019s for you. We\u2019ll walk through how Cloudflare handles WebSockets and gRPC, what Nginx really needs to make those connections durable, and how to deploy changes without bumping users off the line. Along the way I\u2019ll share the exact Nginx bits I rely on, some practical keep\u2011alive defaults, a simple heartbeat strategy for long\u2011lived streams, and a calm way to roll updates without drama. Let\u2019s make your connections boringly stable.<\/p>\n<div id=\"toc_container\" class=\"toc_transparent no_bullets\"><p class=\"toc_title\">\u0130&ccedil;indekiler<\/p><ul class=\"toc_list\"><li><a href=\"#Whats_Actually_Happening_on_the_Wire_And_Where_Timeouts_Hide\"><span class=\"toc_number toc_depth_1\">1<\/span> What\u2019s Actually Happening on the Wire (And Where Timeouts Hide)<\/a><\/li><li><a href=\"#Serving_WebSockets_via_Cloudflare_Nginx_Headers_Timeouts_and_Heartbeats\"><span class=\"toc_number toc_depth_1\">2<\/span> Serving WebSockets via Cloudflare: Nginx Headers, Timeouts, and Heartbeats<\/a><\/li><li><a href=\"#Serving_gRPC_via_Cloudflare_HTTP2_Nginx_grpc_pass_and_Streaming_Stability\"><span class=\"toc_number toc_depth_1\">3<\/span> Serving gRPC via Cloudflare: HTTP\/2, Nginx grpc_pass, and Streaming Stability<\/a><\/li><li><a href=\"#KeepAlive_EndtoEnd_Cloudflare_Nginx_Upstream\"><span class=\"toc_number toc_depth_1\">4<\/span> Keep\u2011Alive End\u2011to\u2011End: Cloudflare \u2194 Nginx \u2194 Upstream<\/a><\/li><li><a href=\"#ZeroDowntime_Reloads_BlueGreen_Tricks_and_Graceful_Drains\"><span class=\"toc_number toc_depth_1\">5<\/span> Zero\u2011Downtime: Reloads, Blue\u2011Green Tricks, and Graceful Drains<\/a><\/li><li><a href=\"#RealWorld_Gotchas_and_the_Fixes_That_Stick\"><span class=\"toc_number toc_depth_1\">6<\/span> Real\u2011World Gotchas and the Fixes That Stick<\/a><ul><li><a href=\"#1_Random_Disconnects_That_Track_With_Quiet_Periods\"><span class=\"toc_number toc_depth_2\">6.1<\/span> 1) \u201cRandom\u201d Disconnects That Track With Quiet Periods<\/a><\/li><li><a href=\"#2_Buffering_Where_Streaming_Is_Expected\"><span class=\"toc_number toc_depth_2\">6.2<\/span> 2) Buffering Where Streaming Is Expected<\/a><\/li><li><a href=\"#3_Works_Locally_Hangs_in_Production\"><span class=\"toc_number toc_depth_2\">6.3<\/span> 3) \u201cWorks Locally, Hangs in Production\u201d<\/a><\/li><li><a href=\"#4_gRPC_Fails_With_Mysterious_Header_Errors\"><span class=\"toc_number toc_depth_2\">6.4<\/span> 4) gRPC Fails With Mysterious Header Errors<\/a><\/li><li><a href=\"#5_Origin_TLS_and_HTTP2_Are_Not_Optional_for_gRPC\"><span class=\"toc_number toc_depth_2\">6.5<\/span> 5) Origin TLS and HTTP\/2 Are Not Optional for gRPC<\/a><\/li><li><a href=\"#6_Observability_Know_When_Sockets_Are_Happy\"><span class=\"toc_number toc_depth_2\">6.6<\/span> 6) Observability: Know When Sockets Are Happy<\/a><\/li><li><a href=\"#7_Security_Between_Nginx_and_Your_App\"><span class=\"toc_number toc_depth_2\">6.7<\/span> 7) Security Between Nginx and Your App<\/a><\/li><li><a href=\"#8_Caching_and_Streaming_Dont_MixBut_Caches_Still_Help\"><span class=\"toc_number toc_depth_2\">6.8<\/span> 8) Caching and Streaming Don\u2019t Mix\u2014But Caches Still Help<\/a><\/li><li><a href=\"#9_Origins_Behind_Firewalls\"><span class=\"toc_number toc_depth_2\">6.9<\/span> 9) Origins Behind Firewalls<\/a><\/li><\/ul><\/li><li><a href=\"#A_Small_Composable_Setup_You_Can_Reuse\"><span class=\"toc_number toc_depth_1\">7<\/span> A Small, Composable Setup You Can Reuse<\/a><\/li><li><a href=\"#Cloudflare_Edge_Settings_Worth_Knowing\"><span class=\"toc_number toc_depth_1\">8<\/span> Cloudflare Edge Settings Worth Knowing<\/a><\/li><li><a href=\"#Bringing_It_All_Together\"><span class=\"toc_number toc_depth_1\">9<\/span> Bringing It All Together<\/a><\/li><\/ul><\/div>\n<h2 id=\"section-1\"><span id=\"Whats_Actually_Happening_on_the_Wire_And_Where_Timeouts_Hide\">What\u2019s Actually Happening on the Wire (And Where Timeouts Hide)<\/span><\/h2>\n<p>Here\u2019s the thing about \u201cone connection drops sometimes\u201d bugs: they rarely live in one place. When you\u2019re serving WebSockets and gRPC behind Cloudflare, you\u2019ve got at least three links in the chain:<\/p>\n<p>First, the client talks to Cloudflare\u2019s edge. Second, the edge talks to your origin\u2014Nginx, in most setups. Third, Nginx speaks to your application upstreams (maybe a cluster, maybe one process). Each hop has its own ideas about how long to wait, whether to reuse connections, and when to give up silently. Let one hop get impatient and your users feel it as random disconnects, even if your app is fine.<\/p>\n<p>WebSockets and gRPC are special because they\u2019re long\u2011lived. WebSockets upgrade an HTTP\/1.1 request into a persistent, bidirectional tunnel. gRPC rides over HTTP\/2 streams and often stays open a while, especially with server streaming. That means we need to keep each leg alive longer than your average page load, avoid buffering that interferes with streaming, and make sure small heartbeats prevent \u201cidle\u201d disconnects. The work is mostly in Nginx timeouts and keep\u2011alive, plus a few key headers\u2014done right, you\u2019ll see errors vanish and CPU calm down because you\u2019re not thrashing connections anymore.<\/p>\n<h2 id=\"section-2\"><span id=\"Serving_WebSockets_via_Cloudflare_Nginx_Headers_Timeouts_and_Heartbeats\">Serving WebSockets via Cloudflare: Nginx Headers, Timeouts, and Heartbeats<\/span><\/h2>\n<p>WebSockets through Cloudflare are straightforward if you respect the upgrade dance and let the tunnel breathe. The browser speaks HTTP\/1.1 and asks to upgrade; Cloudflare passes that along to your origin. Nginx\u2019s job is to honor the upgrade, keep the pipe open, and not get antsy if things go quiet for a bit.<\/p>\n<p>In practice, three things tend to break WebSockets: missing Upgrade\/Connection headers, too\u2011short proxy_read_timeout (the idle timer from Nginx to your upstream), and proxies trying to \u201chelp\u201d by buffering. Here\u2019s a clean, production\u2011friendly Nginx block that has treated me well:<\/p>\n<pre class=\"language-nginx line-numbers\"><code class=\"language-nginx\"># http context\nmap $http_upgrade $connection_upgrade {\n    default upgrade;\n    ''      close;\n}\n\n# Recommend sane keep-alive defaults for general traffic too\nkeepalive_timeout 65s;\nkeepalive_requests 10000;\n\nserver {\n    listen 443 ssl http2;\n    server_name example.com;\n\n    # SSL bits omitted for brevity\n\n    # WebSocket endpoint (e.g., \/ws)\n    location \/ws\/ {\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection $connection_upgrade;\n        proxy_set_header Host $host;\n\n        # Don\u2019t buffer WS frames; let the tunnel flow freely\n        proxy_buffering off;\n        proxy_request_buffering off;\n\n        # Idle: how long Nginx will wait without seeing data from upstream\n        # Make this comfortably larger than your application heartbeat interval\n        proxy_read_timeout 3600s;\n        proxy_send_timeout 3600s;\n\n        # Optional: if you terminate TLS at Nginx and pass to another upstream\n        proxy_pass http:\/\/ws_upstream;\n    }\n}\n\nupstream ws_upstream {\n    # Your app servers; keepalive helps under fan-out\n    server 127.0.0.1:9001 max_fails=3 fail_timeout=10s;\n    keepalive 128;\n}\n<\/code><\/pre>\n<p>A couple of notes. The map for <strong>$connection_upgrade<\/strong> ensures that if the request doesn\u2019t ask to upgrade, we don\u2019t force a weird \u201cConnection: upgrade\u201d header into the mix. The <strong>proxy_read_timeout<\/strong> is your friend: if your app sends a heartbeat every minute, set this to something like an hour so Nginx won\u2019t close the socket because it got bored. And while <strong>proxy_buffering off<\/strong> isn\u2019t strictly required for WebSockets (Nginx turns the connection into a tunnel on upgrade), I prefer to set it explicitly in WebSocket locations. It\u2019s one fewer thing to wonder about at 2 AM.<\/p>\n<p>On the Cloudflare side, WebSockets are supported natively, and they\u2019ll stay open as long as the connection is active. If you see disconnects under light load, it\u2019s almost always an idle timeout in the origin chain. I also like to set a tiny heartbeat at the application level\u2014just a ping every 20\u201330 seconds\u2014to keep intermediaries aware that the connection is alive. If your app implements ping\/pong already, make sure the interval aligns with your <strong>proxy_read_timeout<\/strong> cushion.<\/p>\n<p>If you want a quick reference from the source, the Cloudflare docs cover the basics of <a href=\"https:\/\/developers.cloudflare.com\/websockets\/\" rel=\"nofollow noopener\" target=\"_blank\">serving WebSockets via Cloudflare<\/a>. It\u2019s worth a skim if you think an edge setting is doing something surprising.<\/p>\n<h2 id=\"section-3\"><span id=\"Serving_gRPC_via_Cloudflare_HTTP2_Nginx_grpc_pass_and_Streaming_Stability\">Serving gRPC via Cloudflare: HTTP\/2, Nginx grpc_pass, and Streaming Stability<\/span><\/h2>\n<p>gRPC adds one big requirement: your origin must speak HTTP\/2 over TLS. Cloudflare will happily proxy gRPC, but when it connects to your origin, it expects an HTTP\/2 capable endpoint. That\u2019s why the Nginx \u201clisten\u201d needs <strong>http2<\/strong> alongside SSL, and why your upstream is proxied with <strong>grpc_pass<\/strong> instead of <strong>proxy_pass<\/strong>.<\/p>\n<p>Here\u2019s a lean Nginx example that supports unary and streaming RPCs, with conservative timeouts that won\u2019t cut off a long stream mid\u2011flow:<\/p>\n<pre class=\"language-nginx line-numbers\"><code class=\"language-nginx\"># http context\nkeepalive_timeout 65s;\nkeepalive_requests 10000;\n\nserver {\n    listen 443 ssl http2;\n    server_name api.example.com;\n\n    # SSL bits omitted for brevity\n\n    # gRPC endpoint (root path typically)\n    location \/ {\n        # gRPC is HTTP\/2-based; use the gRPC proxy module\n        grpc_set_header Host $host;\n        grpc_read_timeout 3600s;\n        grpc_send_timeout 3600s;\n\n        # If your upstream is plaintext h2c, use grpc:\/\/; for TLS, use grpcs:\/\/\n        grpc_pass grpc:\/\/grpc_upstream;\n    }\n}\n\nupstream grpc_upstream {\n    # Use IPs or DNS; keepalive helps under load and reduces handshake churn\n    server 127.0.0.1:50051 max_fails=3 fail_timeout=10s;\n    keepalive 128;\n}\n<\/code><\/pre>\n<p>Two gotchas come up a lot with gRPC. First, header sizes: gRPC can carry metadata, and some frameworks love to stuff context in there. If you see errors about headers being too big, bump <strong>large_client_header_buffers<\/strong> in the http context. Second, streaming: many people assume a \u201c100\u2011second timeout\u201d somewhere means gRPC can\u2019t stream; in reality, the trick is to keep data flowing. If the server is actively sending frames or the client is keeping the stream alive, you\u2019re fine. When in doubt, add a small periodic message on long\u2011idle streams to keep intermediaries from marking the connection idle.<\/p>\n<p>Cloudflare\u2019s own write\u2011up on <a href=\"https:\/\/developers.cloudflare.com\/network\/protocols\/grpc\/\" rel=\"nofollow noopener\" target=\"_blank\">proxying gRPC through Cloudflare<\/a> is a solid counterpart to this section. For Nginx specifics, I like keeping the <a href=\"https:\/\/nginx.org\/en\/docs\/http\/ngx_http_grpc_module.html\" rel=\"nofollow noopener\" target=\"_blank\">Nginx gRPC proxy module reference<\/a> handy\u2014they document every gRPC timeout knob you\u2019ll care about.<\/p>\n<h2 id=\"section-4\"><span id=\"KeepAlive_EndtoEnd_Cloudflare_Nginx_Upstream\">Keep\u2011Alive End\u2011to\u2011End: Cloudflare \u2194 Nginx \u2194 Upstream<\/span><\/h2>\n<p>Think of keep\u2011alive as the social glue for connections. When it\u2019s present and polite, everyone relaxes and conversations keep going. When it\u2019s missing, you see a lot of awkward hangs, retries, and CPU spikes from handshake churn.<\/p>\n<p>Between Cloudflare and Nginx, keep\u2011alive is automatic if your origin cooperates. Nginx\u2019s <strong>keepalive_timeout<\/strong> and <strong>keepalive_requests<\/strong> shape the lifetime of the server\u2011side connection pool that Cloudflare will reuse. Cloudflare opens fewer connections and reuses them more predictably when the origin advertises sensible keep\u2011alive, so you\u2019ll notice a calmer connection footprint right away. I like 65 seconds for <strong>keepalive_timeout<\/strong> and a five\u2011figure <strong>keepalive_requests<\/strong> to avoid needless churn.<\/p>\n<p>Between Nginx and your app, enable upstream keep\u2011alive. It\u2019s just <strong>keepalive 128<\/strong> in the <em>upstream<\/em> block, plus the matching read\/send timeouts in your location. Behind the scenes this creates a connection cache per worker, which saves your app from constantly handshaking new HTTP\/1.1 or HTTP\/2 sessions. For gRPC h2c, Nginx still manages those connections sensibly.<\/p>\n<p>One more dial that matters in busy clusters: <strong>fail_timeout<\/strong> and <strong>max_fails<\/strong>. If an upstream instance blips during deploy or GC, let Nginx mark it as failed for a short window and try others, rather than peppering it with new streams that will just die. I like three failures in ten seconds as a starting point, then adjust based on how your app behaves under restarts.<\/p>\n<h2 id=\"section-5\"><span id=\"ZeroDowntime_Reloads_BlueGreen_Tricks_and_Graceful_Drains\">Zero\u2011Downtime: Reloads, Blue\u2011Green Tricks, and Graceful Drains<\/span><\/h2>\n<p>When you\u2019re serving long\u2011lived connections, the way you deploy matters more than usual. A reload that bounces every worker at once can feel like a mass disconnection to users mid\u2011call. Luckily, Nginx is really good at graceful reloads if you give it the right signals.<\/p>\n<p>The everyday move is a <strong>graceful reload<\/strong> (SIGHUP). Nginx spins up new workers with the new config, lets existing workers finish in\u2011flight requests, and then retires them. For short requests, this is indistinguishable from no downtime. For long streams, I add <strong>worker_shutdown_timeout<\/strong> to the main config so old workers have time to wind down:<\/p>\n<pre class=\"language-bash line-numbers\"><code class=\"language-bash\"># main (top-level) context\nworker_shutdown_timeout 30s;\n<\/code><\/pre>\n<p>That one line is a quiet hero. It gives WebSocket and gRPC streams a chance to finish, and if you\u2019re doing a blue\u2011green rollout behind Nginx, it smooths handover between app versions.<\/p>\n<p>Speaking of blue\u2011green: my favorite no\u2011drama pattern is to register both versions in the upstream, wait until the new one is warm, and then remove the old servers with a reload. During the overlap, connection stickiness (even without cookies) tends to keep existing streams pinned, and new connections drift toward the new pool. If you can stagger instance restarts instead of bouncing the whole set at once, you\u2019ll barely see a blip in your charts.<\/p>\n<p>For applications that need extra defense against slow drains, add a small <strong>proxy_next_upstream<\/strong> policy for transient errors. Just be careful not to retry non\u2011idempotent methods\u2014most gRPC operations are safe if your client and server embrace idempotency or have request IDs, but know your calls before you turn on aggressive retries.<\/p>\n<p>If you\u2019re curious how I approach risk across the stack, the same calm, stepwise rollout style I use for Nginx shows up in other playbooks too. For example, the way I write DR plans and verify backups in real life is here: <a href=\"https:\/\/www.dchost.com\/blog\/en\/felaket-kurtarma-plani-nasil-yazilir-rto-rpoyu-kafada-netlestirip-yedek-testleri-ve-runbooklari-gercekten-calisir-hale-getirmek\/\">How I Write a No\u2011Drama DR Plan<\/a>. Different topic, same spirit: remove surprises before they happen.<\/p>\n<h2 id=\"section-6\"><span id=\"RealWorld_Gotchas_and_the_Fixes_That_Stick\">Real\u2011World Gotchas and the Fixes That Stick<\/span><\/h2>\n<h3><span id=\"1_Random_Disconnects_That_Track_With_Quiet_Periods\">1) \u201cRandom\u201d Disconnects That Track With Quiet Periods<\/span><\/h3>\n<p>Every time I see a WebSocket drop right around quiet minutes, it\u2019s almost always an idle timeout mismatch. The fix is two\u2011part: raise <strong>proxy_read_timeout<\/strong> on the relevant location, and add a tiny heartbeat at the app level so the world sees occasional traffic. For gRPC streams, you can send a small keep\u2011alive frame or metadata ping on a reasonable cadence. Don\u2019t flood the connection; a light touch is enough to keep intermediaries from folding their arms and walking away.<\/p>\n<h3><span id=\"2_Buffering_Where_Streaming_Is_Expected\">2) Buffering Where Streaming Is Expected<\/span><\/h3>\n<p>Some reverse proxies will try to buffer request bodies for you\u2014that\u2019s sensible for form posts, but ghastly for streams. In WebSocket locations, explicitly set <strong>proxy_request_buffering off<\/strong>. For gRPC, you don\u2019t usually need to tweak buffering, but if you see memory pressure during large unary calls, consider tuning <strong>client_body_buffer_size<\/strong> and rejecting outsized payloads early with <strong>client_max_body_size<\/strong> to keep things predictable.<\/p>\n<h3><span id=\"3_Works_Locally_Hangs_in_Production\">3) \u201cWorks Locally, Hangs in Production\u201d<\/span><\/h3>\n<p>Local tests often go direct to the app, so you miss the origin step entirely. When you move behind Cloudflare and Nginx, you add two layers of keep\u2011alive and two sets of timeouts. Test end\u2011to\u2011end once you add the edge. I like to run a small synthetic client that does a long server\u2011stream and a quiet WebSocket session for a few hours. If those two stay happy, production users will too.<\/p>\n<h3><span id=\"4_gRPC_Fails_With_Mysterious_Header_Errors\">4) gRPC Fails With Mysterious Header Errors<\/span><\/h3>\n<p>When gRPC metadata gets chatty, the HTTP\/2 header envelope can exceed defaults. Bump <strong>large_client_header_buffers<\/strong> in http context, and make sure your framework or middleware isn\u2019t shoving giant tokens into headers every call. If you\u2019re doing user auth at the edge, pass a compact token down and fetch heavier user context server\u2011side rather than stapling it onto every request as metadata.<\/p>\n<h3><span id=\"5_Origin_TLS_and_HTTP2_Are_Not_Optional_for_gRPC\">5) Origin TLS and HTTP\/2 Are Not Optional for gRPC<\/span><\/h3>\n<p>Cloudflare expects an HTTP\/2\u2011capable origin for gRPC. If your origin only speaks HTTP\/1.1, clients will connect fine to the edge but hit weird failures at the last hop. Enable <strong>http2<\/strong> on your TLS listener and use <strong>grpc_pass<\/strong>. Cloudflare\u2019s docs on <a href=\"https:\/\/developers.cloudflare.com\/network\/protocols\/grpc\/\" rel=\"nofollow noopener\" target=\"_blank\">gRPC through Cloudflare<\/a> are the shortest path to checking your boxes.<\/p>\n<h3><span id=\"6_Observability_Know_When_Sockets_Are_Happy\">6) Observability: Know When Sockets Are Happy<\/span><\/h3>\n<p>When you\u2019re troubleshooting streams, logs and metrics feel like a conversation with your app. I lean on connection state counters, upstream failure rates, and latency histograms. If you need a calm way to centralize logs and set useful alerts without overbuilding, I wrote a detailed guide that dovetails nicely here: <a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-log-yonetimi-nasil-rayina-oturur-grafana-loki-promtail-ile-merkezi-loglama-tutma-sureleri-ve-alarm-kurallari\/\">VPS Log Management Without the Drama<\/a>. It shows the exact alert rules that catch slow leaks and rising 502s before users do.<\/p>\n<h3><span id=\"7_Security_Between_Nginx_and_Your_App\">7) Security Between Nginx and Your App<\/span><\/h3>\n<p>Don\u2019t let \u201cit\u2019s internal\u201d become your default posture. If your app worker nodes live on another network segment, consider mTLS between Nginx and the upstreams. It\u2019s a very doable upgrade and pays for itself the first time a rogue process tries to talk to your service. If you want a friendly walkthrough, start with <a href=\"https:\/\/www.dchost.com\/blog\/en\/nginx-ve-caddyde-mtls-nasil-kurulur-mikroservislerde-sertifika-dogrulamanin-tatli-sirlari\/\">So, Why mTLS? A Story About Trust Between Machines<\/a>.<\/p>\n<h3><span id=\"8_Caching_and_Streaming_Dont_MixBut_Caches_Still_Help\">8) Caching and Streaming Don\u2019t Mix\u2014But Caches Still Help<\/span><\/h3>\n<p>WebSockets and gRPC streams aren\u2019t cacheable, but the rest of your site is. One trick I use a lot: cache the stuff around your streaming features so the origin breathes easier. If your marketing pages and static JSON endpoints are snappy, your servers have more headroom for the long\u2011lived connections. If you\u2019re curious how far a small cache can go, here\u2019s a calm intro to microcaching that pairs nicely with the setup in this post: <a href=\"https:\/\/www.dchost.com\/blog\/en\/nginx-mikro-onbellekleme-ile-php-uygulamalarini-ucurmak-1-5-sn-cache-bypass-ve-purge-ne-zaman-nasil\/\">How Nginx Microcaching Makes PHP Feel Instantly Faster<\/a>.<\/p>\n<h3><span id=\"9_Origins_Behind_Firewalls\">9) Origins Behind Firewalls<\/span><\/h3>\n<p>When you tighten the origin firewall, remember that Cloudflare\u2019s edge will connect from known IP ranges. Allow those ranges to 443 and keep your origin otherwise closed. If you\u2019d like a clean way to express those rules\u2014especially on IPv6\u2014this cookbook has the kind of examples you can paste without sweating: <a href=\"https:\/\/www.dchost.com\/blog\/en\/nftables-ile-vps-guvenlik-duvari-rehberi-rate-limit-port-knocking-ve-ipv6-kurallari-nasil-tatli-tatli-kurulur\/\">The nftables Firewall Cookbook for VPS<\/a>.<\/p>\n<h2 id=\"section-7\"><span id=\"A_Small_Composable_Setup_You_Can_Reuse\">A Small, Composable Setup You Can Reuse<\/span><\/h2>\n<p>Let me share a trimmed \u201cfits\u2011in\u2011your\u2011head\u201d layout I\u2019ve used in dockerized environments for teams shipping mixed HTTP, WebSockets, and gRPC off a single domain. One Nginx container terminates TLS, handles WebSockets at <code>\/ws\/<\/code>, speaks gRPC on a subdomain, and forwards to app services by name. The point isn\u2019t to copy every line\u2014just to see where the keep\u2011alive and timeouts live so you don\u2019t forget them during the rush of a deploy.<\/p>\n<pre class=\"language-nginx line-numbers\"><code class=\"language-nginx\"># \/etc\/nginx\/nginx.conf\nuser nginx;\nworker_processes auto;\npid \/var\/run\/nginx.pid;\n\nevents {\n    worker_connections 10240;\n}\n\nhttp {\n    include       \/etc\/nginx\/mime.types;\n    default_type  application\/octet-stream;\n\n    # Logging &amp; gzip omitted for brevity\n\n    # Keep-alive that plays nicely with Cloudflare\n    keepalive_timeout 65s;\n    keepalive_requests 10000;\n\n    map $http_upgrade $connection_upgrade {\n        default upgrade;\n        ''      close;\n    }\n\n    upstream ws_upstream { server app-ws:9001 max_fails=3 fail_timeout=10s; keepalive 128; }\n    upstream grpc_upstream { server app-grpc:50051 max_fails=3 fail_timeout=10s; keepalive 128; }\n\n    server {\n        listen 443 ssl http2;\n        server_name example.com;\n        # ssl_certificate \/etc\/ssl\/certs\/fullchain.pem;\n        # ssl_certificate_key \/etc\/ssl\/private\/privkey.pem;\n\n        # Regular HTTP endpoints\n        location \/api\/ {\n            proxy_http_version 1.1;\n            proxy_set_header Host $host;\n            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n            proxy_set_header X-Forwarded-Proto $scheme;\n            proxy_read_timeout 120s;\n            proxy_send_timeout 120s;\n            proxy_pass http:\/\/app-http:8080;\n        }\n\n        # WebSockets\n        location \/ws\/ {\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection $connection_upgrade;\n            proxy_set_header Host $host;\n            proxy_buffering off;\n            proxy_request_buffering off;\n            proxy_read_timeout 3600s;\n            proxy_send_timeout 3600s;\n            proxy_pass http:\/\/ws_upstream;\n        }\n    }\n\n    # Separate vhost for gRPC if you like a clean boundary\n    server {\n        listen 443 ssl http2;\n        server_name api.example.com;\n        # ssl certs ...\n        location \/ {\n            grpc_set_header Host $host;\n            grpc_read_timeout 3600s;\n            grpc_send_timeout 3600s;\n            grpc_pass grpc:\/\/grpc_upstream;\n        }\n    }\n\n    # Graceful worker shutdown helps long-lived streams during reloads\n    # (This directive lives in main context in newer Nginx; shown here for clarity.)\n}\n<\/code><\/pre>\n<p>If you\u2019re packaging services with Docker, this pattern drops in next to your existing compose pretty easily. My write\u2011up on calm Docker Compose patterns for Nginx\u2011fronted stacks might spark some ideas if you\u2019re building this from scratch: <a href=\"https:\/\/www.dchost.com\/blog\/en\/docker-compose-ile-wordpress-nginx-mariadb-redis-nasil-tatli-tatli-akiyor-kalici-hacimler-otomatik-yedek-ve-guncelleme-akisi\/\">WordPress on Docker Compose, Without the Drama<\/a>. It\u2019s about WordPress, sure, but the Nginx and deployment rhythms are the same.<\/p>\n<h2 id=\"section-8\"><span id=\"Cloudflare_Edge_Settings_Worth_Knowing\">Cloudflare Edge Settings Worth Knowing<\/span><\/h2>\n<p>You don\u2019t need a thousand toggles to make this work, but there are a few edge settings worth a quick look in the Cloudflare dashboard:<\/p>\n<p>First, make sure your DNS record is proxied (the orange cloud), or none of the WebSocket\/gRPC magic will matter\u2014clients will hit the origin directly. Second, if you\u2019re using page rules or transform rules, keep an eye out for anything that strips or rewrites headers in ways that break upgrade requests or HTTP\/2 negotiation. Finally, watch analytics for origin error spikes during deploys; if you see a flurry of 520\/522 around releases, it\u2019s a sign to lengthen graceful shutdown windows or stagger upstream restarts more gently.<\/p>\n<p>If you want a concise official reference for WebSocket support, Cloudflare\u2019s doc on <a href=\"https:\/\/developers.cloudflare.com\/websockets\/\" rel=\"nofollow noopener\" target=\"_blank\">WebSockets at the edge<\/a> is a good one to bookmark.<\/p>\n<h2 id=\"section-9\"><span id=\"Bringing_It_All_Together\">Bringing It All Together<\/span><\/h2>\n<p>In my experience, reliable WebSockets and gRPC behind Cloudflare come down to five simple habits that you\u2019ll set once and then forget:<\/p>\n<p>One, add the upgrade headers and use the right proxy module (proxy_pass for WebSockets, grpc_pass for gRPC). Two, stretch your timeouts for long\u2011lived connections and line them up with a small app\u2011level heartbeat so \u201cidle\u201d never means \u201cdead.\u201d Three, enable keep\u2011alive across the board so Cloudflare reuses connections to your origin, and your origin reuses connections to your upstreams. Four, deploy with grace\u2014reload Nginx, give workers room to finish, and stagger app restarts. Five, keep an eye on logs and small, meaningful alerts; they\u2019ll tell you when you\u2019ve got the rhythm right.<\/p>\n<p>Once you do these, the random disconnect gremlins tend to vanish. Your graphs flatten, your CPU has fewer spikes, and your users stop noticing your infrastructure\u2014which is my favorite compliment. If you want to go deeper on related areas, two pieces I often share alongside this guide are the mTLS walkthrough for secure service\u2011to\u2011service traffic (<a href=\"https:\/\/www.dchost.com\/blog\/en\/nginx-ve-caddyde-mtls-nasil-kurulur-mikroservislerde-sertifika-dogrulamanin-tatli-sirlari\/\">why mTLS is worth it<\/a>) and the practical Loki playbook for logs and alerts (<a href=\"https:\/\/www.dchost.com\/blog\/en\/vps-log-yonetimi-nasil-rayina-oturur-grafana-loki-promtail-ile-merkezi-loglama-tutma-sureleri-ve-alarm-kurallari\/\">how to centralize logs without the drama<\/a>). And if you\u2019re curious about wringing easy wins from the non\u2011streaming parts of your stack, this little ode to microcaching has saved many sites a lot of money: <a href=\"https:\/\/www.dchost.com\/blog\/en\/nginx-mikro-onbellekleme-ile-php-uygulamalarini-ucurmak-1-5-sn-cache-bypass-ve-purge-ne-zaman-nasil\/\">The 1\u20135 Second Miracle<\/a>.<\/p>\n<p>Hope this was helpful! If you tune these settings and still see hiccups, drop me a note about your heartbeat interval, the read timeouts on each hop, and how you\u2019re rolling deploys\u2014I\u2019m happy to help trace the path. See you in the next post.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>I still remember the first time a client pinged me late on a Friday: \u201cOur dashboards freeze every few minutes\u2026 and the mobile app drops calls randomly.\u201d The stack looked clean at first glance\u2014Cloudflare in front, Nginx on the origin, a mix of WebSocket streams and a shiny new gRPC backend. But buried in the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1678,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-1677","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-teknoloji"],"_links":{"self":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/1677","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/comments?post=1677"}],"version-history":[{"count":0,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/posts\/1677\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media\/1678"}],"wp:attachment":[{"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/media?parent=1677"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/categories?post=1677"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dchost.com\/blog\/en\/wp-json\/wp\/v2\/tags?post=1677"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}