<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cassiano Aquino</title>
    <description>Protecting the weak against null pointer exceptions, motorcyles, skydiving and technology.</description>
    <link>https://syshero.org/</link>
    <atom:link href="https://syshero.org/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 27 Apr 2026 10:44:03 +0000</pubDate>
    <lastBuildDate>Mon, 27 Apr 2026 10:44:03 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Transparent agents</title>
        <description>&lt;p&gt;Hello everyone! Has been a long time since I last posted here, but hey! as they say better late than never!&lt;/p&gt;

&lt;p&gt;As many of you can imagine, based on my content, I’ve always been a homelab enthusiast, and the last addition to my homelab was a DGX Spark from Nvidia.&lt;/p&gt;

&lt;p&gt;I remember since the first time I saw a computer the first thing that came to my mind was:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How can I make something like this? How does this computer-thing works? How can I make it do what I want?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this is what pushed me to learn about things, how to build them, and obviously it could not be different when we talk about AI.&lt;/p&gt;

&lt;p&gt;I see all these big companies doing fun things and I was thinking how could I do something similar? How can I make the user experience better? More powerful without requiring the user to install a lot of things on their machines.&lt;/p&gt;

&lt;p&gt;From my previous experience I was thinking about transparent proxies and how they work and then I had the idea:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What if we could make a transparent proxy that has an agent on it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And here we are! Let me share with you how the concept works.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;The reason for this whole pattern is simple: every chat client I already use speaks the OpenAI Chat Completions API, Open WebUI, Cursor, the OpenAI SDK, n8n’s HTTP node, all of them. None of them speak any “agent protocol” because there isn’t one yet. So whatever I build has to look like a model from the wire, and still behave like an agent inside.&lt;/p&gt;

&lt;p&gt;I’m calling this a &lt;em&gt;transparent agent&lt;/em&gt;. The transparent proxy in HTTP is the same idea: a proxy that sits between client and origin and intercepts traffic without the client having to know it’s there. Squid, HAProxy, Envoy, NGINX all support it. The client doesn’t configure anything special, doesn’t speak a different protocol, just makes the same request it always made, and a proxy in the middle does whatever it does while the client sees a normal response back.&lt;/p&gt;

&lt;p&gt;Same idea, one layer up:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A transparent proxy sits between client and origin without requiring client configuration. The client thinks it’s talking directly to the origin; the proxy is invisible to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;A transparent agent sits between client and model without requiring client configuration. The client thinks it’s talking directly to a chat completions model; the agent is invisible to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent reaches out to MCP servers, retrieves from a vector store, summarizes old turns, dispatches tools, and assembles a reply. The client posts to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/v1/chat/completions&lt;/code&gt;, gets a streaming response, and never finds out that the response came from three MCP calls, a vector retrieval, and four turns of tool dispatching. All of the complexity stays on the server.&lt;/p&gt;

&lt;p&gt;I tried a few other names before this one stuck. &lt;em&gt;Smart model&lt;/em&gt; and &lt;em&gt;wrapped model&lt;/em&gt; got the wire surface right but the substance wrong, this thing is an agent, not a model. &lt;em&gt;Agent gateway&lt;/em&gt; and &lt;em&gt;agentic gateway&lt;/em&gt; are basically taken by agentgateway.dev (a real project, but it does inter-agent A2A/MCP routing, not what I’m building). &lt;em&gt;Wire agent&lt;/em&gt; and &lt;em&gt;hosted agent&lt;/em&gt; were close but they don’t say what’s invisible, which is the whole point. &lt;em&gt;Transparent agent&lt;/em&gt; hangs off a real networking term, and the rest of the post leans on it.&lt;/p&gt;

&lt;h2 id=&quot;why-a-callback-proxy-is-the-wrong-place&quot;&gt;Why a callback proxy is the wrong place&lt;/h2&gt;

&lt;p&gt;The first version of this in my homelab ran on top of &lt;a href=&quot;https://docs.litellm.ai/&quot;&gt;LiteLLM&lt;/a&gt; with a callback hook. LiteLLM is a great router, and lets you hook into each request and response with a Python plugin. So the hook intercepted the prompt, did a vector search, injected a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;retrieved_context&amp;gt;&lt;/code&gt; system message, did a small amount of cleanup on the way out, and called it done.&lt;/p&gt;

&lt;p&gt;This works for one round. It breaks the moment the model wants to call a tool, get a result, and call another tool. The hook fires once per HTTP request. The agent loop the OpenAI SDK runs is multiple HTTP requests with tool messages in between. You can fake one round by parsing the response, dispatching the tool yourself, and stitching a follow-up call. At that point you have reimplemented the agent loop inside a callback that wasn’t designed to host one. The state ends up split across the proxy and the client and nobody owns the conversation. In practice that meant Open WebUI re-sending the entire chat history on every turn, including the base64 of any chart we had emitted. We paid for the same image five turns in a row.&lt;/p&gt;

&lt;p&gt;The lesson everyone hits eventually: a callback layer is the wrong place to host an agent loop or any meaningful conversational state. You need something that &lt;em&gt;owns&lt;/em&gt; the conversation. A callback hook can’t, by design. A transparent agent does, also by design.&lt;/p&gt;

&lt;h2 id=&quot;what-does-fit&quot;&gt;What does fit&lt;/h2&gt;

&lt;p&gt;A small HTTP service that exposes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/v1/chat/completions&lt;/code&gt; and runs the &lt;a href=&quot;https://openai.github.io/openai-agents-python/&quot;&gt;OpenAI Agents SDK&lt;/a&gt; inside. The agent reaches out to MCP servers, retrieves from pgvector, and streams back. From the client’s side it’s still a chat completions endpoint, it points at a model name I picked. Internally it’s a proper agent loop with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_turns=10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Three pipelines ended up sitting around the agent:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;sequenceDiagram
    participant Client as Open WebUI / curl
    participant GW as Transparent agent
    participant Pipe as Pipeline
    participant Agent as Agents SDK + MCPs
    Client-&amp;gt;&amp;gt;GW: POST /v1/chat/completions
    GW-&amp;gt;&amp;gt;Pipe: run_request(ctx)
    Note over Pipe: cache replay, time injection,&amp;lt;br/&amp;gt;RAG retrieval, rotation
    GW-&amp;gt;&amp;gt;Agent: Runner.run_streamed(...)
    Agent--&amp;gt;&amp;gt;GW: text deltas, tool calls, reasoning
    GW-&amp;gt;&amp;gt;Pipe: run_chunks(ctx, content)
    Note over Pipe: harmony strip, image substitution
    GW-&amp;gt;&amp;gt;Pipe: run_completion(ctx, content)
    Note over Pipe: disclaimer, audit
    GW--&amp;gt;&amp;gt;Client: SSE stream (reasoning then content)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_request&lt;/code&gt; mutates the message list before the agent runs. The conversation cache replays here, retrieved context gets injected, a temporal “today is” line gets added, and rotation drops the oldest pairs if the conversation got too long. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_chunk&lt;/code&gt; operates on the assembled assistant text after the agent finishes, before the client sees it. Strip out tokenizer artifacts, swap placeholder strings for image data URLs, redact things you should never have allowed the model to emit in the first place. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;on_completion&lt;/code&gt; is the last gate. Append a disclaimer in the user’s language, write an audit row, send.&lt;/p&gt;

&lt;p&gt;Each processor is a Python class, declared by name on the YAML, loaded on the right pipeline. Adding a new one is a one-line registry entry plus a file. Removing one is a one-line config edit. The orchestration code stays small, around 180 lines in the version I have now, and the agent assembly is 90 lines, mostly MCP wiring.&lt;/p&gt;

&lt;p&gt;The rule that makes this work: every piece of behavior the client never sees lives in a processor, and every processor either runs &lt;em&gt;before the agent&lt;/em&gt; (to shape what the agent sees) or &lt;em&gt;after the agent&lt;/em&gt; (to shape what the client sees). The agent stays clean. It runs the loop and that’s it.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;

&lt;p&gt;This post is the opener. The next one is about the conversation cache and KV-prefix stability, since Open WebUI re-sends the full chat history on every turn and the upstream prompt cache invalidates the moment we inject anything the client never saw. The fix is keeping our own canonical view of the conversation, keyed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chat_id&lt;/code&gt;, and replaying that instead of what the client sent.&lt;/p&gt;

&lt;p&gt;After that, background context compaction. When the cached prefix grows past a threshold, an out-of-band task summarizes the oldest turns into one system message and atomically swaps it in, race-safe via Redis WATCH/MULTI, and the current request never blocks waiting for it.&lt;/p&gt;

&lt;p&gt;Then the smaller context-reduction tricks: image substitution at egress so the model never sees base64, placeholder-form caching so we don’t pay for the same chart twice, pair-aligned rotation as a floor when even compaction can’t keep up.&lt;/p&gt;

&lt;p&gt;Then the reasoning channel, streaming reasoning tokens before content so the o1-style “thinking” panel works in clients that already render &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reasoning_content&lt;/code&gt;, without any client-side code.&lt;/p&gt;

&lt;p&gt;And finally the metrics worth keeping. Token-inflation per processor is the one chart that pays for itself, the rest is usual SRE hygiene.&lt;/p&gt;

&lt;p&gt;That is all for this post, it is quite long already! I will be documenting my struggles trying to justify why I spent so much money on a DGX Spark, if this is something that interests you, make yourself at home, you are a welcome guest!&lt;/p&gt;

&lt;p&gt;Until next post!&lt;/p&gt;
</description>
        <pubDate>Sun, 26 Apr 2026 09:00:00 +0000</pubDate>
        <link>https://syshero.org/2026-04-26-transparent-agents/</link>
        <guid isPermaLink="true">https://syshero.org/2026-04-26-transparent-agents/</guid>
        
        <category>ai</category>
        
        <category>agents</category>
        
        <category>devops</category>
        
        
      </item>
    
      <item>
        <title>NGINX - CORS with a single if</title>
        <description>&lt;p&gt;Hello everyone, long time no see!&lt;/p&gt;

&lt;p&gt;Today while working on a PR, I was thinking about how to do CORS without using ifs on NGINX.&lt;/p&gt;

&lt;p&gt;If you are not familiar with the reasons to try to avoid using IF, you should take a look at this &lt;a href=&quot;https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/&quot;&gt;post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as usual, &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; comes at our rescue!&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Sadly, I could not remove all IFs from the configuration as I required to use return 204 for the HTTP method OPTIONS.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Origin whitelist&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_origin&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$allowed_origin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt;             &lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;&quot;~*\.?test\.com&quot;&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~*\.?example\.com&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Methods&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request_method&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;OPTIONS&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;allowed_origin&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;options&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;GET&quot;&lt;/span&gt;     &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;allowed_origin&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;get&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;POST&quot;&lt;/span&gt;    &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;allowed_origin&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;post&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;allowed_origin&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Access-Control-Allow-Origin, if cors true add header.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acao&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~^true.+?&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_origin&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Access-Control-Allow-Credentials, and method is GET/POST/OPTIONS and cors true add header.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acac&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~^true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(options|get|post)&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Access-Control-Allow-Methods, and method is GET/POST/OPTIONS and cors true add header.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~^true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(options|get|post)&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;GET,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POST,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;OPTIONS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Access-Control-Allow-Headers, and method is GET/POST/OPTIONS and cors true add header.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acah&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~^true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(options|get|post)&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Keep-Alive,User-Agent,ETag,Last-Modified,Vary,If-Modified-Since,Cache-Control,Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Access-Control-Max-Age, and method is OPTIONS and cors true add header.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$cors&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acma&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;trueoptions&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;1728000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default_server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;reuseport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;expires&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;24h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.html&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.htm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Access-Control-Allow-Origin&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acao&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Access-Control-Allow-Credentials&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Access-Control-Allow-Methods&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Access-Control-Allow-Headers&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acah&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;Access-Control-Max-Age&apos;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$acma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;OPTIONS&quot;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kn&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;204&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One interesting behavior that allows this configuration to work is the fact that &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header&quot;&gt;add_header&lt;/a&gt; will not add headers if the value is empty.&lt;/p&gt;

&lt;p&gt;With that in mind, we can use &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;maps&lt;/a&gt; without a default value to return empty, which disable headers when they are not necessary.&lt;/p&gt;

&lt;p&gt;If you are only serving static assets, all the references to POST can be removed from the configuration as NGINX returns 405 if you try to POST to a static asset.&lt;/p&gt;

</description>
        <pubDate>Fri, 20 Sep 2019 09:00:10 +0000</pubDate>
        <link>https://syshero.org/2019-09-20-cors-and-ifisevil/</link>
        <guid isPermaLink="true">https://syshero.org/2019-09-20-cors-and-ifisevil/</guid>
        
        <category>nginx</category>
        
        <category>devops</category>
        
        <category>linux</category>
        
        
      </item>
    
      <item>
        <title>DNS Round Robin, Consul and Java</title>
        <description>&lt;p&gt;Hello everyone! Today, for a change, I will be talking about Consul from Hashicorp instead of NGINX.&lt;/p&gt;

&lt;p&gt;I will start with the assumption that you’re aware of what a DNS round robin is and how it’s supposed to work and evolve from there.&lt;/p&gt;

&lt;p&gt;I would like to start saying that the “problem” we faced, it’s not related to Consul, but the solution that we looked for was for Consul.
&lt;!--more--&gt;
Today Rafael while troubleshooting some uneven load distribution across his services, noticed an unexpected behavior.&lt;/p&gt;

&lt;p&gt;He had his service correctly configured in Consul, the DNS response from Consul was returning the expected information, but still, only one node was receiving all the requests.&lt;/p&gt;

&lt;p&gt;First of all, we started to take a look at java DNS lookup functionality to understand what could cause this issue, especially that both of us saw Greg Banks presentation about Java’s internal DNS caching, which threw us out a little bit.&lt;/p&gt;

&lt;p&gt;After doing some tests disabling Java’s internal DNS caching and still having the same issue, we started to break the problem into pieces and test piece by piece, which we should’ve done since the beginning.&lt;/p&gt;

&lt;p&gt;We started doing direct DNS queries to Consul to verify that the DNS response was what we expected to be, and it was, so we know that the problem is not Consul.&lt;/p&gt;

&lt;p&gt;The next step on our DNS path is the local dnsmasq, which also proved to be working as expected after some simple DNS queries.&lt;/p&gt;

&lt;p&gt;Now we were to a point where the DNS path leads us back to Java-land, which we tested before and looked OK.&lt;/p&gt;

&lt;p&gt;But just to be sure that Java was the issue and not something else, we tried to use curl, after all the DNS path for both will be the same, and to our surprise, curl had the same behavior that Java had.&lt;/p&gt;

&lt;p&gt;After this test, we can safely assume, that what’s causing this issue, it’s not only on Java-land, but we also know that’s NOT on DNS land, so what is the problem? Where to go now?&lt;/p&gt;

&lt;p&gt;While trying to search for more information, regarding DNS round robin, we found this post written in 2012 by Daniel Stenberg curl lead developer, which gave us more clues about our problem.&lt;/p&gt;

&lt;p&gt;The short version is that most applications used gethostbyname to make DNS requests, which will just give you a randomly ordered list of hosts, which makes DNS round robin work as expected.&lt;/p&gt;

&lt;p&gt;But gethostbyname was built to support IPv4 and with IPv6
a new function was required, this is when getaddrinfo comes to play.&lt;/p&gt;

&lt;p&gt;The “problem” is that getaddrinfo implements RFC3484 which returns the address always in a specific order, thus breaking DNS round robin, which relied on the random order from gethostbyname.&lt;/p&gt;

&lt;p&gt;The idea is that when using getaddrinfo, the application should control the desired behavior, for example, grabbing a random IP from the list, or trying in order until you find one that’s working.&lt;/p&gt;

&lt;p&gt;In one hand, this is good as it gives applications more control over the desired behavior, but on the other hand, it makes the behavior inconsistent as each application do whatever they want.&lt;/p&gt;

&lt;p&gt;Now that we knew what we were facing, we had some options, change our internal application to control this, or figure out a way to make our application work as it worked on the good old gethostbyname times.&lt;/p&gt;

&lt;p&gt;And for our surprise, Consul had a clever workaround for this issue, which limit the size of the answer, and returns a random IP from the list instead of returning the full list and assume that the client application handles it as expected.&lt;/p&gt;

&lt;p&gt;This has some trade-offs, for example, DNS TTL needs to be shorter as the DNS RR will rely on returning a different address for each query, which may increase the load on your DNS infrastructure and Consul.&lt;/p&gt;

&lt;p&gt;But this will solve our issue until we have a more consistent behavior from applications using getaddrinfo.&lt;/p&gt;

&lt;p&gt;For some extra information, you can also take a look at this post at Consul’s mailing list.&lt;/p&gt;

&lt;p&gt;That’s all for today!&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Apr 2018 13:06:22 +0000</pubDate>
        <link>https://syshero.org/2018-04-26-dns-round-robin-consul-and-java/</link>
        <guid isPermaLink="true">https://syshero.org/2018-04-26-dns-round-robin-consul-and-java/</guid>
        
        <category>devops</category>
        
        <category>consul</category>
        
        <category>dns</category>
        
        
      </item>
    
      <item>
        <title>NGINX+ - Replicated Key-Value Store</title>
        <description>&lt;p&gt;Today I would like to show how two NGINX+ modules can work together to create a replicated and load balanced RESTfull key-value storage.&lt;/p&gt;

&lt;p&gt;If you have any question, not only about NGINX, that you think that I can answer for you, feel free to leave a comment!
&lt;!--more--&gt;
I’ve been monitoring NGINX questions on Twitter, but let’s say that people have a really bad time asking questions, especially when they have a limited amount of characters to do so.&lt;/p&gt;

&lt;p&gt;The current implementation of NGINX+ API has support for a key-value store, but not to replicate it, but nothing that can’t be solved if we use the mirror module.&lt;/p&gt;

&lt;p&gt;The concept that I will show below has two NGINX+ nodes, registered in Consul.&lt;/p&gt;

&lt;p&gt;The servers have a service called nginx-kv, and tags that identify which node is the master or slave of the cluster.&lt;/p&gt;

&lt;p&gt;This is optional and can be done using plain DNS, but I’ve used Consul because it’s another powerful technology that can augment NGINX functionality.&lt;/p&gt;

&lt;p&gt;On the master node we have the following configuration:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resolver&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8600&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;valid=2s&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ipv6=off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;resolver_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;keyval_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=default:32k&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;state=default.json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;keyval&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg_text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$text1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/replicated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;mirror&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/mirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write=on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/mirror&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Replication-Source&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;limit_except&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;GET&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HEAD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://slave.nginx-kv.service.consul&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The API is accessible via the URI /replicated, and every request is mirrored to /mirror.&lt;/p&gt;

&lt;p&gt;On the /mirror location, a header X-Replication-Source with the value master is set, and I will get back to it as it’s really important.&lt;/p&gt;

&lt;p&gt;And any request that is not GET or HEAD is mirrored via proxy_pass to the slave node.&lt;/p&gt;

&lt;p&gt;We have no interest in mirroring GETs and HEADs to the mirror as this would waste resources.&lt;/p&gt;

&lt;p&gt;On the slave we have the following configuration:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;keyval_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=default:32k&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;state=default.json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;keyval&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$arg_text&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$text1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$mmethod&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_x_replication_source$request_method&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$mmethod&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;^(POST|PUT|DELETE|PATCH)$&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;307&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://master.nginx-kv.service.consul&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/replicated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write=on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first thing to note on the slave, we have a variable that we concatenate the header X-Replicated-Source with the request_method, this way if a request comes from the master nginx, it will not be affected by the if clause.&lt;/p&gt;

&lt;p&gt;The if clause then checks from any write operations, that is not coming from the master node, and redirect them to the master server.&lt;/p&gt;

&lt;p&gt;This is a similar approach to LDAP updateref; the slave server is aware that it’s a slave, and redirect writes to the master.&lt;/p&gt;

&lt;p&gt;With this in place, a simple load balancer can be added in front of the two nodes, and reads will be load balanced and writes will be sent to the master server.&lt;/p&gt;

&lt;p&gt;I quickly presented this concept at nginx.conf 2017 in Portland, where I said that I would share more information in a blog post, took some time, but here it is!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;EDIT 2026-04-24.&lt;/strong&gt; The industry has largely moved away from the “master/slave” naming I used above. The mechanics of this design are identical whether you call the nodes “master/replica”, “primary/replica”, or “writer/follower”; the technique works the same. I’m leaving the post unchanged to preserve the 2018 voice, but if you’re adapting this for anything new, please adopt a replacement pair.&lt;/p&gt;
</description>
        <pubDate>Tue, 24 Apr 2018 09:00:10 +0000</pubDate>
        <link>https://syshero.org/2018-04-24-nginx-replicated-key-value-store/</link>
        <guid isPermaLink="true">https://syshero.org/2018-04-24-nginx-replicated-key-value-store/</guid>
        
        <category>nginx</category>
        
        <category>devops</category>
        
        <category>linux</category>
        
        
      </item>
    
      <item>
        <title>NGINX+ Automatic PURGE when content is changed</title>
        <description>&lt;p&gt;One of the biggest challenges for content caching is the expiration of this content, too short lived and your cache is not effective, too long, and you will serve stale data.&lt;/p&gt;

&lt;p&gt;The sweet spot for caching is exactly to cache the content while it’s valid, which is hard as you may not know for how long your content will be valid at the moment that’s being cached.
&lt;!--more--&gt;
To help with that NGINX+ has a cache purge functionality, that allows applications to send a request to NGINX+ and ask that specific content to be removed from the cache.&lt;/p&gt;

&lt;p&gt;This requires extra application logic, which is fine, but sometimes you need to be able to purge content for applications that are not aware of the caching in front of it.&lt;/p&gt;

&lt;p&gt;For applications that use the same URIs to read and write data based on methods, for example, let’s imagine that we have a URI /profile/syshero.&lt;/p&gt;

&lt;p&gt;If a GET is sent to /profile/syshero, the application will render and serve the output to the client.&lt;/p&gt;

&lt;p&gt;If a PUT/POST/DELETE is sent to /profile/syshero, the application will do a write operation and change this content&lt;/p&gt;

&lt;p&gt;This concept is similar to how REST API works, which makes this approach compatible with a large number of REST applications.&lt;/p&gt;

&lt;p&gt;The desired solution would:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Cache or serve cached content for GET/HEAD requests&lt;/li&gt;
  &lt;li&gt;Purge cached content for any other HTTP method e.g.: POST, PUT, DELETE, PATCH, MKCOL, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following configuration achieves that by using the NGINX+ mirror module together with the purge option.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resolver&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8600&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;valid=2s&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ipv6=off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;resolver_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/tmp/nginx&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keys_zone=default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max_size=50m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=backend&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Cache-Status&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$upstream_cache_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;mirror&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/mirror&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;mirror_request_body&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_valid&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/mirror&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;&quot;HEAD|GET&quot;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kn&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;204&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass_request_body&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Content-Length&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_purge&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$request_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This configuration receives any request to /, forward to the backends servers and also mirrors this request to the location /mirror.&lt;/p&gt;

&lt;p&gt;The location /mirror will return a 204 for GET and HEAD methods as we want to avoid duplicating all our requests to the backend, and also to avoid purging the cache on every request, and for any other method that is not GET or HEAD purge the cache for the specific URI.&lt;/p&gt;

&lt;p&gt;As can be seen on the following test:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@host:~# curl &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; http://localhost/data 2&amp;gt;&amp;amp;1 | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;X-Cache-Status
&amp;lt; X-Cache-Status: HIT
root@host:~# curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; POST &lt;span class=&quot;nt&quot;&gt;--data&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test=1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; http://localhost/data 2&amp;gt;&amp;amp;1 | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;X-Cache-Status
root@host:~# curl &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; http://localhost/data 2&amp;gt;&amp;amp;1 | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;X-Cache-Status
&amp;lt; X-Cache-Status: MISS
root@host:~# curl &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; http://localhost/data 2&amp;gt;&amp;amp;1 | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;X-Cache-Status
&amp;lt; X-Cache-Status: HIT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Simple as that!&lt;/p&gt;

&lt;p&gt;One important thing to note is that as you can see, different variables are required on both location blocks as the proxy_cache_key, and why is that? This is necessary because of how the mirror module works. To be able to have the same value on both blocks it was necessary to use $uri and $request_uri.&lt;/p&gt;

&lt;p&gt;I would like to thank all of NGINX+ Support team for the constant help and support but especially Dmitry Pryadko that helped me figure our how to filter out methods as limit_except was not working as expected.&lt;/p&gt;

&lt;p&gt;Also, I would like to thank Bruno Paiuca for asking this question on twitter which got me thinking about on how to solve this problem.&lt;/p&gt;

&lt;p&gt;@nginx you need an option to purge cache after post/put/delete to works with our Rest APIs. proxy_cache_purge_after POST, PUT, DELETE— Bruno Paiuca (@BPaiuca) February 10, 2018&lt;/p&gt;

&lt;p&gt;Thanks, everyone! See you next time!&lt;/p&gt;
</description>
        <pubDate>Tue, 17 Apr 2018 09:00:33 +0000</pubDate>
        <link>https://syshero.org/2018-04-17-nginx-automatic-purge-when-content-is-changed/</link>
        <guid isPermaLink="true">https://syshero.org/2018-04-17-nginx-automatic-purge-when-content-is-changed/</guid>
        
        <category>devops</category>
        
        <category>nginx</category>
        
        <category>linux</category>
        
        <category>caching</category>
        
        
      </item>
    
      <item>
        <title>NGINX - Unique Request Identifier</title>
        <description>&lt;p&gt;A really useful information to have while debugging applications is an end-to-end request id.&lt;/p&gt;

&lt;p&gt;To be able to achieve that you need not only NGINX but also application changes, to forward the ids but also to log them.
&lt;!--more--&gt;
On this post, I would like to share how to implement the NGINX side.&lt;/p&gt;

&lt;p&gt;One way to achieve that is setting a header for both the client side and to the upstream called X-Request-ID for example.&lt;/p&gt;

&lt;p&gt;The behavior expect is that, if X-Request-ID is set, NGINX will forward the same value to the next upstream, if the header is not set, NGINX will generate a random request identifier and add it to the request.&lt;/p&gt;

&lt;p&gt;To achieve that we use a map configuration as can be seen in the following configuration.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_x_request_id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uuid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;request_id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;~*&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;http_x_request_id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Request-ID&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Request-ID&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backend.local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On my previous implementation, we used a Lua piece of code to generate the unique request identifier, and this had specific formatting similar to:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;12345678-1234-1234-1234-1234&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To guarantee backward compatibility, I used an NGINX map to format the $request_id variable to a format that matches my old setup.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request_id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$formatted_id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;&quot;~*&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(?&amp;lt;p1&amp;gt;[0-9a-f]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)(?&amp;lt;p2&amp;gt;[0-9a-f]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)(?&amp;lt;p3&amp;gt;[0-9a-f]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)(?&amp;lt;p4&amp;gt;[0-9a-f]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)(?&amp;lt;p5&amp;gt;.*)&lt;/span&gt;$&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;p1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;p2&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;p3&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;p4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;-&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;p5&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_x_request_id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uuid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt;   &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;formatted_id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;~*&lt;/span&gt;        &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;$&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;http_x_request_id&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using map, you can apply regular expressions to variables and transform the variables based on the regular expression.&lt;/p&gt;

&lt;p&gt;This is one of the many reasons why I think map is one of the most versatile and powerful configuration options for NGINX.&lt;/p&gt;

&lt;p&gt;I hope this helps you!&lt;/p&gt;
</description>
        <pubDate>Fri, 13 Apr 2018 10:00:20 +0000</pubDate>
        <link>https://syshero.org/2018-04-13-nginx-unique-request-identifier/</link>
        <guid isPermaLink="true">https://syshero.org/2018-04-13-nginx-unique-request-identifier/</guid>
        
        <category>devops</category>
        
        <category>linux</category>
        
        <category>nginx</category>
        
        
      </item>
    
      <item>
        <title>NGINX+ Chained PURGE</title>
        <description>&lt;p&gt;After a long winter, I’m here again to post about my favorite subject, wanna guess? NGINX! (Like it was hard to guess)&lt;/p&gt;

&lt;p&gt;I will start with a story, which may or may not be based in real life, you get to choose.
&lt;!--more--&gt;
Once upon the time, Mr. J worked in a big company that used NGINX as their caching layer.&lt;/p&gt;

&lt;p&gt;As the company grew, so their infrastructure, and now they needed to run multiple NGINXs (What’s the plural of NGINX BTW?), and seemed to be an easy task, turned into a complex problem.&lt;/p&gt;

&lt;p&gt;Their applications used NGINX as a cache, and now doing cache PURGE was a lot more complex than it was before, as they needed to do it on every NGINX server as the caches were isolated from each other.&lt;/p&gt;

&lt;p&gt;Mr. J was also worried about how this would work in the Cloud™, what about Autoscaling Groups?&lt;/p&gt;

&lt;p&gt;All that fancy functionalities that I can leverage BUT now it’s much harder to do PURGEs.&lt;/p&gt;

&lt;p&gt;How do I keep a list of the servers?
Do I need to call the Cloud™ API?&lt;/p&gt;

&lt;p&gt;After some research, he had some ideas, one was to use shared storage between the caches, but he felt that this would increase the complexity even more and steered away from this option.&lt;/p&gt;

&lt;p&gt;What about using Lua? After some research, he had some issues to overcome.&lt;/p&gt;

&lt;p&gt;First, How to maintain a list of the NGINX nodes dynamically.&lt;/p&gt;

&lt;p&gt;We have a way to solve this, as we use NGINX+ and Consul, we could use an upstream to have a list of the NGINX servers.&lt;/p&gt;

&lt;p&gt;Sadly, Lua has no access to the list of servers in an upstream without extra modules, which almost drove him away from the solution!&lt;/p&gt;

&lt;p&gt;After thinking harder, Mr. J didn’t want to add unnecessary modules to his NGINX+, which made him remember that NGINX+ has an API that can be used to query the upstream servers list.&lt;/p&gt;

&lt;p&gt;But then, another problem! Lua has no access to external HTTP requests without extra Lua modules!&lt;/p&gt;

&lt;p&gt;But this one was easily solved! Lua has a function to do internal HTTP requests, ngx.location.capture(), which will not work to call other external endpoints, but if we join forces between ngx.location.capture and proxy_pass we can make it work.&lt;/p&gt;

&lt;p&gt;Mr. J continued to move forward with the mission in his mind of doing it using only what he had available to avoid increasing the complexity of his system.&lt;/p&gt;

&lt;p&gt;After a lot of research and hard work (not really, it was done in a couple of hours, but don’t tell anyone.) Mr. J managed to come up with a solution for his problems.&lt;/p&gt;

&lt;p&gt;Let’s imagine a simple NGINX+, which works well with a single cache node:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/tmp/nginx&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keys_zone=default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max_size=50m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=backend&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Cache-Status&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$upstream_cache_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_valid&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On this example, NGINX+ uses Consul to populate the backends upstream server list.&lt;/p&gt;

&lt;p&gt;Nothing special here, no PURGE or anything just yet!&lt;/p&gt;

&lt;p&gt;Now, to allow PURGE, Mr. J decided to go with an invalid internal domain, and this was the configuration used.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/tmp/nginx&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keys_zone=default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max_size=50m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=backend&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request_method&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$purge_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;PURGE&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger.local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_purge&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$purge_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basically, to purge any content, he can even use cli tools like curl:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt; PURGE &lt;span class=&quot;nt&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Host: purger.local&apos;&lt;/span&gt; http://127.0.0.1/uri/to/purge/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When executing a PURGE, NGINX+ will return a 204 No Content.&lt;/p&gt;

&lt;p&gt;But still, Mr. J needed to turn this simple configuration in something that would be aware of other NGINX+ caches, and forward the PURGE requests to the other instances.&lt;/p&gt;

&lt;p&gt;Finally  Mr. J he had a solution to his problem:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;resolver&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8600&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;valid=2s&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ipv6=off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;resolver_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;proxy_cache_path&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/tmp/nginx&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;levels=1:2&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keys_zone=default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max_size=50m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backends&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=backend&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;lua_package_cpath&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;;&lt;span class=&quot;k&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/usr/share/nginx/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;add_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;X-Cache-Status&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$upstream_cache_status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_valid&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$request_method&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$purge_method&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;PURGE&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger.local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;172.16&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.0.0/24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# NGINX+ Network&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write=on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^/proxy_to/(?&amp;lt;dest&amp;gt;[^&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/]+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;resolver&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.1.1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ipv6=off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;resolver_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_method&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PURGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;purger.local&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dest&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_purge&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$purge_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^/purger/(?&amp;lt;purge_uri&amp;gt;.*)&lt;/span&gt;$ &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;kn&quot;&gt;content_by_lua_block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kn&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cjson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;require(&quot;cjson&quot;)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cjson.decode(ngx.location.capture(&apos;/api/3/http/upstreams/caches/servers&apos;).body)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ngx.say(&apos;Purging&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;URI:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.var.purge_uri)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k,v&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pairs(caches)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;parent&apos;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;then&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.location.capture(&apos;/proxy_to/&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;server&apos;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.var.purge_uri,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;share_all_vars&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;ngx.say(&apos;Purging&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;server:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;server&apos;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;res.status&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;)&apos;)&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s break these into pieces and explain the important parts, first NGINX+ has an upstream called caches configuration, that’s not used by any proxy_pass:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service.consul&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;service=cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is used as a catalog to allow instances to find each other.&lt;/p&gt;

&lt;p&gt;Another important part is to declare where the NGINX+ Lua module will find the cjson module, as Mr. J used Ubuntu, after simply doing an apt-get install lua-cjson, he added the following line to the configuration:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;lua_package_cpath&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;;&lt;span class=&quot;k&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And now it comes the important part, the most complex one:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;status_zone&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;purger.local&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;allow&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;172.16&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.0.0/24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# NGINX+ Network&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;deny&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write=on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^/proxy_to/(?&amp;lt;dest&amp;gt;[^&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/]+&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;resolver&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.1.1&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ipv6=off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;resolver_timeout&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_method&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PURGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_set_header&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;purger.local&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dest&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_key&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_purge&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$purge_method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;^/purger/(?&amp;lt;purge_uri&amp;gt;.*)&lt;/span&gt;$ &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;kn&quot;&gt;content_by_lua_block&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kn&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cjson&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;require(&quot;cjson&quot;)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;caches&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cjson.decode(ngx.location.capture(&apos;/api/3/http/upstreams/caches/servers&apos;).body)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ngx.say(&apos;Purging&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;URI:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.var.purge_uri)&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k,v&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pairs(caches)&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;parent&apos;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;then&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.location.capture(&apos;/proxy_to/&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;server&apos;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;/&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ngx.var.purge_uri,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;share_all_vars&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;ngx.say(&apos;Purging&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;server:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v[&apos;server&apos;]&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;(&apos;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;res.status&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;)&apos;)&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When any content needs to be purged on all nodes, a PURGE request to /purger/index.html should be issued on using the host purger.local.&lt;/p&gt;

&lt;p&gt;This will trigger the Lua code running on this location, that will call NGINX+ API and fetch a list of servers on the upstream caches.&lt;/p&gt;

&lt;p&gt;For each server on the upstream list, Lua will execute an internal ngx.location.capture() to a proxying location, that will then send the request to the destination server. And here it’s the solution in action:&lt;/p&gt;

&lt;p&gt;NGINX+ register themselves to Consul as the service named cache, and they are immediately available to NGINX+ cluster to be used as servers on the upstream configuration.&lt;/p&gt;

&lt;p&gt;With this solution, the PURGE request can be sent to any NGINX+ server, and it will forward the PURGE request to all other NGINX+ nodes in the pool.&lt;/p&gt;

&lt;p&gt;And Mr. J lived happily ever after.&lt;/p&gt;

&lt;p&gt;I hope that Mr. J adventure helps you solve your caching issues!&lt;/p&gt;

&lt;p&gt;See you next time!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;EDIT 2026-04-24.&lt;/strong&gt; The Lua code above calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/3/http/upstreams/caches/servers&lt;/code&gt;. Since this post was written, NGINX+ has iterated on its API (current stable is v8+). Older version paths still work because NGINX+ keeps backward compatibility on the versioned prefix, but if you’re authoring new config you should point at the current version available on your build. Check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location /api { api write=on; }&lt;/code&gt; output at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api&lt;/code&gt; for the versions it advertises.&lt;/p&gt;
</description>
        <pubDate>Thu, 12 Apr 2018 17:06:13 +0000</pubDate>
        <link>https://syshero.org/2018-04-12-nginx-chained-purge/</link>
        <guid isPermaLink="true">https://syshero.org/2018-04-12-nginx-chained-purge/</guid>
        
        <category>devops</category>
        
        <category>consul</category>
        
        <category>nginx</category>
        
        <category>syadmin</category>
        
        <category>caching</category>
        
        
      </item>
    
      <item>
        <title>Thinking outside of the box with NGINX series - proxy_cache_bypass command</title>
        <description>&lt;p&gt;Hello, everyone! Time to get back to our series of posts about NGINX!&lt;/p&gt;

&lt;p&gt;This time, let’s talk about an old friend, the proxy_cache_bypass command, which we used multiple times in my examples, but I never dedicated a post to it, so it’s time to fix this error!
&lt;!--more--&gt;
At first glimpse, proxy_cache_bypass feels like a simple command, one where you can bypass caching when necessary, but after reading the documentation with more attention, there was a piece of it that caught my attention.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note that the response from the back-end is still eligible for caching.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So basically proxy_cache_bypass not only allows you to bypass the cache but also will update our cache with the new file working similarly to a cache purge/refresh functionality.&lt;/p&gt;

&lt;p&gt;If you want to not update cached files, you should use proxy_no_cache instead, which will have the desired behavior.&lt;/p&gt;

&lt;p&gt;Let’s imagine that you want to bypass your cache when the requests are from your office, to do this you can use the geo module together with proxy_cache_bypass, and as a bonus, the cache will be updated as your office browse through your website.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;geo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$office_networks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;192.168.0.0/24&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$office_networks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache_valid&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;any&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_cache&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mycache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://my_backends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another really useful feature of proxy_cache_bypass is that you can pass multiple variables as an argument to it and if any is set it will enable the bypass action.&lt;/p&gt;

&lt;p&gt;Some other posts I did that I used proxy_cache_bypass to update our cache:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2016-06-13-thinking-outside-of-the-box-with-nginx-series-split_clients/&quot;&gt;Thinking outside of the box with NGINX series - split_clients command&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2013-11-29-nginx-passive-cache-invalidation/&quot;&gt;NGINX passive cache invalidation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2013-05-01-nginx-cache-purge-emulation/&quot;&gt;NGINX “cache purge” emulation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you in the next post!&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;
</description>
        <pubDate>Wed, 29 Jun 2016 12:31:23 +0000</pubDate>
        <link>https://syshero.org/2016-06-29-thinking-outside-of-the-box-with-nginx-series-proxy_cache_bypass/</link>
        <guid isPermaLink="true">https://syshero.org/2016-06-29-thinking-outside-of-the-box-with-nginx-series-proxy_cache_bypass/</guid>
        
        <category>nginx</category>
        
        <category>devops</category>
        
        <category>solution</category>
        
        <category>smart</category>
        
        
      </item>
    
      <item>
        <title>imapfilter - cleaning up your mailbox and Gmail tricks</title>
        <description>&lt;p&gt;Hello friends, For the ones of you that do not know imapfilter, you should check it out, but basically, imapfilter is an IMAP client where you can write a set of rules and apply this rules to your mailbox.&lt;/p&gt;

&lt;p&gt;imapfilter rules are written in Lua, a really simple and powerful programming language.
&lt;!--more--&gt;
To start you should create a folder in your home directory called imapfilter, to do so you use the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; ~/.imapfilter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The configuration file should be saved at ~/.imapfilter/config.lua, after having this configuration file in place, just by running imapfilter will be enough to have your configuration executed.&lt;/p&gt;

&lt;p&gt;To make things a little bit more interesting, let’s dissect my imapfilter configuration, where I will explain some basic features and some Gmail particularities that you should be aware if you use it.&lt;/p&gt;

&lt;p&gt;Let’s start with some basic options that I use, and I will explain why.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1200&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expunge&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;options.timeout: will specify a timeout for imapfilter to wait for the IMAP server response, if you have huge mailboxes OR if you’re planning to use the special “All mail” folder in Gmail, it’s a good idea to have a big timeout as operations may take a while.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;options.create: this will create folders that you reference in the configuration if they don’t exist.options.subscribe: this will subscribe to the created folders, basically when you create a folder does not mean that it needs to appear on the list, this will make it appear.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;options.expunge: deleting messages in folders just mark this messages for deletion, moving the messages to trash, this will remove deleted messages, this behavior can be different based on IMAP server configuration, this is the most common configuration thou.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s define our account login options.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IMAP&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;imap.gmail.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;your-login@your-domain.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;your-password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ssl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;auto&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This block will create an object called account1, which represents our mail account.&lt;/p&gt;

&lt;p&gt;It’s a self-explanatory block, but I do have a remark if you use 2-factor authentication on google you need to generate an application password and put it here as your password will not work.&lt;/p&gt;

&lt;p&gt;Now, let’s start to see some basic rules I use, you will need to change the examples to match your needs, as this one was made based on my personal needs.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;archive_read_older&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Archiving read messages on INBOX unflagged and older than &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;archive_read_older&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; days.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INBOX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_seen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INBOX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_unflagged&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INBOX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;archive_read_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On this block, I’ve created a code that, in the case of Gmail, archives read and unflagged messages older than a specified period, for example in this example 30 days.&lt;/p&gt;

&lt;p&gt;For Gmail, the function delete_messages() archive instead of delete, keep this in mind while doing your configuration.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cal_delete_older&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Removing calendar messages older than &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cal_delete_older&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; days.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/All Mail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contain_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sender&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;calendar-notification@google.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/All Mail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cal_delete_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/Trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we will remove meeting invites older than 90 days, don’t worry if you accepted, it will be on your calendar and will not be deleted.&lt;/p&gt;

&lt;p&gt;Note as we want to delete this messages, instead of using delete_messages() we are moving the messages to the Trash folder on Gmail.&lt;/p&gt;

&lt;p&gt;Now let’s say we want to delete messages from some senders and based on some subjects, instead of having individual code for each, it’s simpler to use arrays to do it.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;senders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;spammer@spammerdomain.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;youtube.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Mailer Delivery System&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;subjects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;out of office&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AFK&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Sick&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;senders&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Removing messages from &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;senders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/All Mail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contain_from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;senders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/Trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subjects&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Removing messages with subject &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subjects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/All Mail&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contain_subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subjects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/Trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again we are moving to Trash, as we want to delete this messages.&lt;/p&gt;

&lt;p&gt;Now let’s clean up some folders where I store automated emails.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;folders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Monitoring/Crons&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Monitoring/Nagios&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folders&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Emptying folder &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/Trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And to finish things up, we need to delete messages on our Trash by using the following code.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Emptying trash folder.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[Gmail]/Trash&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;select_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One thing that I like to do is to move messages older than a specific period out of my INBOX to a “Cleanup” folder.&lt;/p&gt;

&lt;div class=&quot;language-lua highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cleanup_older&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Moving messages older than &quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cleanup_older&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;..&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; on INBOX to Cleanup.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;INBOX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cleanup_older&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move_messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;account1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Cleanup&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All these examples should be put on the same file, called ~/.imapfilter/config.lua, or you can use includes if you want, but now you have a good baseline on how to clean up your mailbox using imapfilter.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;EDIT 2026-04-24.&lt;/strong&gt; Changed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl = &quot;tls1&quot;&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl = &quot;auto&quot;&lt;/code&gt; in the account block. When this post was written TLSv1.0 was still common, but Gmail and most other IMAP providers have long since required TLSv1.2+. Leaving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;tls1&quot;&lt;/code&gt; in a 2026 config means the connection will fail to negotiate.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

&lt;p&gt;See you on the next post!&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Jun 2016 14:03:35 +0000</pubDate>
        <link>https://syshero.org/2016-06-19-imapfilter-cleaning-up-your-mailbox-and/</link>
        <guid isPermaLink="true">https://syshero.org/2016-06-19-imapfilter-cleaning-up-your-mailbox-and/</guid>
        
        <category>devops</category>
        
        <category>productivity</category>
        
        <category>email</category>
        
        
      </item>
    
      <item>
        <title>Thinking outside of the box with NGINX series - map command</title>
        <description>&lt;p&gt;I don’t think this is thinking outside the box, but I could not do a series of posts about useful commands without talking about &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt;, arguably one of the most useful commands on NGINX, especially that I’m sure map will be used in most of our posts.
&lt;!--more--&gt;
You should think about &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; as a “switch” statement like many programming languages have, it’s how you will be able to take advantage of it.&lt;/p&gt;

&lt;p&gt;Basically, the syntax for &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; is the following:&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$variable_to_evaluate&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$return_variable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt; &lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;value_to_match&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value_to_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt; &lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;value_to_return_by_default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where the variable_to_evaluate can be ANY variable accessible by NGINX, which includes HTTP headers, Cookies, etc.&lt;/p&gt;

&lt;p&gt;You can think of &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; as a good substitute for most “ifs” that you plan to use, especially considering that if is evil, basically &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; is a safe replacement for if in most of the situations.&lt;/p&gt;

&lt;p&gt;Let’s review our rate limiting example that we used &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_geo_module.html&quot;&gt;geo&lt;/a&gt; to create a whitelist, now we will use &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; to do the same thing, if the client sends a cookie called “whitelistrl”, he will bypass the rate limiting, but remember, this is not a good idea for rate limiting control as cookies can be easily injected to any HTTP request.&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;192.168.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;192.168.0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;limit_req_zone&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$limit_var&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=limit_default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rate=10r/s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_cookie&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$limit_var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$binary_remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;~whitelistrl&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;limit_req&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=limit_default&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;burst=10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another functionality is that &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; can be nested, one map can call another map or even combine &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_geo_module.html&quot;&gt;geo&lt;/a&gt;/&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt;/&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_split_clients_module.html&quot;&gt;split_clients&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s try another example, what about using &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; and &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_geo_module.html&quot;&gt;geo&lt;/a&gt; to allow us to only whitelist clients from our customer network that contains the whitelistrl cookie? Sounds like fun!&lt;/p&gt;

&lt;div class=&quot;language-nginx highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;upstream&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;192.168.0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;192.168.0.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;limit_req_zone&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$limit_var&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=limit_default:10m&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rate=10r/s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_cookie&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$limit_var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$binary_remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;~whitelistrl&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$network_var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;geo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$network_var&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$binary_remote_addr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;200.200.200.0/24&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;limit_req&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;zone=limit_default&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;burst=10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http://production&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One thing to note thou, the order of the nesting matters, &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; accepts nesting other commands, but this is not true for all commands, for example, if we had tried to call &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; from &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_geo_module.html&quot;&gt;geo&lt;/a&gt;, it was not going to work, so always test before deploying to production!&lt;/p&gt;

&lt;p&gt;Also, you can see examples of &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_map_module.html&quot;&gt;map&lt;/a&gt; usage in posts right here on my blog!&lt;/p&gt;

&lt;p&gt;Some examples are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2015-07-03-persistent-backend-selection-using-cookies-with/&quot;&gt;Persistent backend selection using cookies with NGINX&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2015-03-28-using-map-for-upstream-configuration-on-nginx/&quot;&gt;Using map for upstream configuration on NGINX&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2013-05-10-disable-nginx-cache-based-on-cookies/&quot;&gt;Disable NGINX cache based on cookies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you on the next post! &lt;/p&gt;

&lt;p&gt;If you have any question or suggestion, use the Ask-O-matic link on the top of the page.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;
</description>
        <pubDate>Sat, 18 Jun 2016 14:41:20 +0000</pubDate>
        <link>https://syshero.org/2016-06-18-thinking-outside-of-the-box-with-nginx-series-map/</link>
        <guid isPermaLink="true">https://syshero.org/2016-06-18-thinking-outside-of-the-box-with-nginx-series-map/</guid>
        
        <category>devops</category>
        
        <category>nginx</category>
        
        <category>smart</category>
        
        <category>solution</category>
        
        
      </item>
    
  </channel>
</rss>
