
[{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/cloudflare/","section":"Tags","summary":"","title":"Cloudflare","type":"tags"},{"content":"So you\u0026rsquo;ve got a Plex server running at home, lifetime Plex Pass and everything, and you want to watch your stuff on the go. You flip on Remote Access, see a cheerful green \u0026ldquo;Fully accessible outside your network\u0026rdquo; message, and think you\u0026rsquo;re done. Amazing. Job done.\nThen you try to connect from your phone and\u0026hellip; nothing.\nIf this sounds familiar, there\u0026rsquo;s a pretty good chance your ISP has you behind CGNAT (Carrier-Grade NAT) - meaning you don\u0026rsquo;t actually have a proper public IP address, and no amount of port forwarding will save you. Let\u0026rsquo;s fix that.\nAm I actually behind CGNAT? # Quick sanity check. Run this on your Plex server:\ncurl ifconfig.me Compare the output with what Plex shows under Settings → Remote Access → Public IP. If they don\u0026rsquo;t match, you\u0026rsquo;re behind CGNAT.\nIf they do match, your problem is likely just a broken port forward, and this guide probably isn\u0026rsquo;t for you. You might have more success with this Troubleshooting Remote Access guide from Plex themselves.\nThe Fix: Cloudflare Tunnel + customConnections # The approach here is simple and split into two parts:\nExpose your Plex server to the internet via a Cloudflare tunnel Tell Plex\u0026rsquo;s discovery system to advertise that tunnel URL to clients Step 1: Set up a Cloudflare tunnel # Install cloudflared on your Plex server and create a tunnel that proxies to localhost:32400, then assign it a public hostname - use whatever you prefer here, but for the purposes of this guide we will assume plex.yourdomain.com. The official Cloudflare docs cover the full setup.\nOnce it\u0026rsquo;s running, verify it actually works before moving on:\ncurl -s \u0026#34;https://plex.yourdomain.com/identity\u0026#34; You should get back some XML with your server info. If you do, the tunnel is good to go.\nStep 2: Grab your Plex token # grep -o \u0026#39;PlexOnlineToken=\u0026#34;[^\u0026#34;]*\u0026#34;\u0026#39; \\ \u0026#34;/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Preferences.xml\u0026#34; Keep the quoted value for later, as we\u0026rsquo;ll be using it to configure your custom connections domain via Plex APIs.\nNote: You might run into permissions issues for the above command. In which case make sure to run it as your Plex user or simply use root permissions through sudo.\nStep 3: Add your tunnel as a Plex custom connection # curl -X PUT \u0026#34;http://localhost:32400/:/prefs?customConnections=https://plex.yourdomain.com:443\u0026amp;X-Plex-Token=YOUR_TOKEN\u0026#34; Notice the :443 at the end - it\u0026rsquo;s important. Without it, Plex \u0026ldquo;helpfully\u0026rdquo; appends whatever random external port it negotiated via UPnP when it tried to setup remote access via your public IP. Normally that would be fine and dandy, except the port isn\u0026rsquo;t open on Cloudflare\u0026rsquo;s side. Explicitly specifying :443 (the port for https) keeps things chugging along and allows you to simply use an https domain as your custom connection.\nStep 4: Confirm your changes were saved # curl -s \u0026#34;http://localhost:32400/:/prefs?X-Plex-Token=YOUR_TOKEN\u0026#34; | grep \u0026#39;customConnections\u0026#39; Expected output:\n\u0026lt;Setting id=\u0026#34;customConnections\u0026#34; label=\u0026#34;Custom server access URLs\u0026#34; summary=\u0026#34;A comma-separated list of URLs (http or https) which are published up to plex.tv for server discovery. IPv6 addresses must specify a port.\u0026#34; type=\u0026#34;text\u0026#34; default=\u0026#34;\u0026#34; value=\u0026#34;https://plex.yourdomain.com:443\u0026#34; hidden=\u0026#34;0\u0026#34; advanced=\u0026#34;1\u0026#34; group=\u0026#34;network\u0026#34; /\u0026gt; Step 5: Finally restart Plex # And keep your fingers crossed as this is the last step! 🤞🏻\nsudo systemctl restart plexmediaserver Step 6: Verify plex.tv is advertising the tunnel # curl -s \u0026#34;https://plex.tv/api/resources?includeHttps=1\u0026amp;X-Plex-Token=YOUR_TOKEN\u0026#34; | grep -o \u0026#39;uri=\u0026#34;[^\u0026#34;]*\u0026#34;\u0026#39; If all went well and nothing exploded along the way, your tunnel URI should appear in the list.\nNow, Plex clients try each configured URI in turn and fall through to the first one that responds. This means that even with a bunch of dead-end local addresses around, Plex clients will eventually land on your tunnel.\nThis also means that local addresses will still be used if available since they should respond the fastest - nice!\nBut Plex tells me my server is \u0026ldquo;Fully accessible\u0026rdquo; - what gives? # Yeah, that status on the settings page can\u0026rsquo;t be trusted. Plex\u0026rsquo;s own remote access self-test can easily return a false positive, tricking you into believing your server is publicly accessible even when it isn\u0026rsquo;t. The only reliable test is pulling out your phone, switching to mobile data, and actually trying to connect. Either that or firing up a VPN.\nThat\u0026rsquo;s it. Enjoy Plex\u0026rsquo;ing around on the go or when away from home in general!\n","date":"27 February 2026","externalUrl":null,"permalink":"/posts/fixing-plex-remote-access-when-youre-stuck-behind-cgnat/","section":"Posts","summary":"A step-by-step guide to getting Plex accessible remotely when your ISP has you trapped behind CGNAT, using a Cloudflare tunnel and a single API call.","title":"Fixing Plex Remote Access When You're Stuck Behind CGNAT","type":"posts"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/","section":"flexicon.dev","summary":"","title":"flexicon.dev","type":"page"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/homelab/","section":"Tags","summary":"","title":"Homelab","type":"tags"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/plex/","section":"Tags","summary":"","title":"Plex","type":"tags"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/self-hosting/","section":"Tags","summary":"","title":"Self-Hosting","type":"tags"},{"content":"","date":"27 February 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"17 April 2024","externalUrl":null,"permalink":"/tags/go/","section":"Tags","summary":"","title":"Go","type":"tags"},{"content":"Have you ever wanted to solve some specific problems in your web application, but JavaScript just wasn\u0026rsquo;t spicy enough or you thought you could use more processing power?\nPerhaps you\u0026rsquo;re a fan of Web Development with Go and wanted to use it for more than just the backend?\nOr maybe you\u0026rsquo;ve been hearing all the hubbub around WebAssembly (Wasm) and wanted to see what the big fuss was all about?\nWell if you even remotely found yourself responding with an \u0026ldquo;eh, I guess\u0026rdquo; to any of the above: look no further. On this page you\u0026rsquo;ll find a minimal, barebones, step-by-step guide of what it takes to get a Go program compiled down to a Wasm module, running in your browser and interacting with it using a simple HTML form.\nWhat is WebAssembly? # To quote the Official Website:\nWebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.\nIn the context of this post, you can think of it as a way for us to run code written in programming languages other than native web JavaScript in the browser.\nThe Goal for today # Let\u0026rsquo;s keep things simple here.\nWe\u0026rsquo;ll build a basic web page with a single input form. The form will take a number from the user and when submitted will display its square root.\nAbout as simple as can be, and straightforward to achieve in vanilla JavaScript. However we\u0026rsquo;re complicated individuals and we don\u0026rsquo;t want to use JavaScripts math or parsing features here - all input parsing and calculations will happen in a Wasm module written in Go instead.\nThe Solution # Looking to jump straight into the solution? I respect your moxie. In that case check out the Git repo.\nThe key files to check are web/index.html to see how the Wasm module is hooked up to our HTML, and the Go Wasm source code itself under cmds/wasm/main.go.\n🚨 Spoiler alert: here\u0026rsquo;s a live version of the finished web page.\nOtherwise, keep on reading and we\u0026rsquo;ll break down the solution step-by-step.\nA simple Wasm Module # The most minimal setup for a Go Wasm module doesn\u0026rsquo;t differ in any way from a generic \u0026ldquo;Hello, World\u0026rdquo; program.\npackage main import ( \u0026#34;fmt\u0026#34; ) func main() { fmt.Println(\u0026#34;Go Wasm module instantiated!\u0026#34;) } The secret sauce is all in how we compile the code from here on out.\nIn a regular Go program, we might do something like go build . , but when targeting Wasm we must specify more arguments and variables.\nGOARCH=wasm GOOS=js go build -o app.wasm . The GOOS variable tells the compiler what operating system we want our target binary to run on, and the GOARCH variables tells it what architecture should be used for the binary. This is also the standard way for cross compiling Go programs, for example to target darwin for macOS and arm64 for Apple silicon architecture.\nRunning the Wasm in a browser # First things first, we\u0026rsquo;ll need a bit of JavaScript to run and instantiate our Go-based Wasm module in the browser - luckily the standard library provides this with every installation of Go, we must simply copy it over to our working directory.\ncp \u0026#34;$(go env GOROOT)/misc/wasm/wasm_exec.js\u0026#34; . Now we have a wasm_exec.js file which we can load in our browser - great, let\u0026rsquo;s do that now.\n\u0026lt;script src=\u0026#34;wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; We can add the above snippet to the head of any HTML document where we want to execute Wasm. Let\u0026rsquo;s create a skeleton document now.\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Using Go in the Browser\u0026lt;/title\u0026gt; \u0026lt;script src=\u0026#34;wasm_exec.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt;\u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Okay, so we\u0026rsquo;ve copied the code that will execute our Wasm module and loaded it in our HTML. Now we have to actually tell the browser to fetch, instantiate and run our app.wasm module. Add the following script tag below the one which loads wasm_exec.js in the head of the document.\n\u0026lt;script\u0026gt; // Creates an instance of the Go wasm_exec class, // streams in the Wasm module using fetch // and finally runs it. 🚀 const go = new Go(); WebAssembly.instantiateStreaming(fetch(\u0026#39;app.wasm\u0026#39;), go.importObject).then((result) =\u0026gt; { go.run(result.instance); }); \u0026lt;/script\u0026gt; Let\u0026rsquo;s test this out and see it come together in action. You should now be able to serve your index.html file along with the wasm_exec.js and app.wasm files using any preferred method. I personally like using gommand for quick demos like this one.\ngo install github.com/sno6/gommand@latest # Now serve assets in your current directory gommand \u0026#39;http.Handle(\u0026#34;/\u0026#34;, http.FileServer(http.Dir(\u0026#34;.\u0026#34;))); fmt.Println(http.ListenAndServe(\u0026#34;:8080\u0026#34;, nil))\u0026#39; The snippet runs a simple Go HTTP File Server based on your current working directory. Once it\u0026rsquo;s running navigate to your web page (if using gommand http://localhost:8080/) and check your console - you should see the following:\n🚀 Great success! Our Wasm module has been successfully loaded, and the standard output from our Go program has landed in our browser console.\nAdding the Form and some Spice 🌶️ # Currently our web page is empty and our Wasm module doesn\u0026rsquo;t really do much - nothing to write home about in any case. Let\u0026rsquo;s fix that.\nHere\u0026rsquo;s the markup for our form:\n\u0026lt;main\u0026gt; \u0026lt;form id=\u0026#34;sqrt-form\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; name=\u0026#34;sqrt-num\u0026#34; id=\u0026#34;sqrt-num\u0026#34; placeholder=\u0026#34;Enter some number\u0026#34; required /\u0026gt; \u0026lt;button type=\u0026#34;submit\u0026#34;\u0026gt;Calculate Square Root\u0026lt;/button\u0026gt; \u0026lt;/form\u0026gt; \u0026lt;div id=\u0026#34;sqrt-answer\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/main\u0026gt; Save it and refresh the page to see\u0026hellip; a bit more, but still not that much.\nLet\u0026rsquo;s quickly spice things up a notch by dropping in a little semantic CSS, courtesy of Pico ✨\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.violet.min.css\u0026#34; /\u0026gt; \u0026hellip; and adding a tiny bit of style to the body tag:\n\u0026lt;body style=\u0026#34;max-width: 900px; margin: 0 auto\u0026#34;\u0026gt; And - channeling my inner Emeril Lagasse - bam!\nNice - that\u0026rsquo;s a little more presentable at least, and immediately matches our earlier mockup.\nExtending the Wasm Module # Now that we have our form set up and markup thoroughly spiced, we can move on back to the actual Wasm code. So far, all it does is print a message to standard output and exits. In order to be able to have it calculate square roots for us, we\u0026rsquo;ll need to do a few things:\nExport a function for the JavaScript runtime to call Keep the Go program running and waiting for calls to said function We can achieve #2 fairly easily by adding a blocking channel receiver call on a perpetually empty channel.\nfunc main() { fmt.Printf(\u0026#34;Go Wasm module instantiated!\\n\u0026#34;) \u0026lt;-make(chan bool) } Cool. Now our program will actually wait around to receive calls. But what sort of calls is it listening for? Well at present: none. Let\u0026rsquo;s change that.\nfunc main() { fmt.Printf(\u0026#34;Go Wasm module instantiated!\\n\u0026#34;) // 1 js.Global().Set(\u0026#34;GoSqrt\u0026#34;, js.FuncOf(func(this js.Value, args []js.Value) any { if len(args) \u0026lt; 1 { // 2 return math.NaN() // 3 } return math.Sqrt(parseFloatJS(args[0])) // 4 })) \u0026lt;-make(chan bool) } Now let\u0026rsquo;s unpack a bit what exactly is happening here:\nWe call the syscall/js.Global func, which allows us to use the standard library to interact with JavaScript\u0026rsquo;s Global namespace (ie: the window object). In this particular case we Set (or declare) a new global function - named GoSqrt - which receives a reference to JavaScript\u0026rsquo;s this scope for the function and a slice of js.Value arguments. We then do some initial argument validation, since our newly declared function requires one argument to be passed to it - the number for which we want to find a square root. If the function was called without any arguments, we return a NaN. If all is well, we call the standard library math.Sqrt func with the argument received from the browser after having parsed it. Ah, I mentioned parsing. Well since JavaScript is not strongly typed, we can\u0026rsquo;t really enforce what type of data gets passed to our function from the browser. What we can do is handle the types passed in Go, and in our case we can handle either a JavaScript number or string type. The js.Value struct provides some nice helpers to make this easier.\nHere\u0026rsquo;s how we do that:\n// parseFloatJS returns a float64 from a js.Value, based on either a `number` or `string` js type. NaN otherwise. func parseFloatJS(v js.Value) float64 { switch v.Type() { case js.TypeNumber: return v.Float() case js.TypeString: if f, err := strconv.ParseFloat(v.String(), 64); err == nil { return f } } return math.NaN() } The logic is fairly basic. We essentially return a float64 when the argument is a JavaScript number type, and if it is a string we use Go\u0026rsquo;s standard strconv package to parse a float64 out of it and return the result if all went well. In all other cases we return NaN.\nWiring everything together # Phew, almost there. The last step is to wire it all up, with some standard DOM JavaScript. What we want to do here is listen to the form submit event, read the current value from our number input and pass it to our global GoSqrt function exported by our Wasm module. Finally we display the returned result from said function below our form.\nSo let\u0026rsquo;s wrap this up by updating our script tag with the following:\nfunction wireItAllUp() { const sqrtNumInput = document.getElementById(\u0026#39;sqrt-num\u0026#39;); const sqrtForm = document.getElementById(\u0026#39;sqrt-form\u0026#39;); const sqrtAnswerDiv = document.getElementById(\u0026#39;sqrt-answer\u0026#39;); sqrtForm.addEventListener(\u0026#39;submit\u0026#39;, function (e) { e.preventDefault(); const value = sqrtNumInput.value; // This is the 👇 part where we call out to Wasm. const result = GoSqrt(value); sqrtAnswerDiv.innerHTML = `🤓 The square root of ${value} is ${result}.\u0026lt;br\u0026gt;So sayeth the Gopher.`; }); } // This statement is unchanged apart from the wireItAllUp call. WebAssembly.instantiateStreaming(fetch(\u0026#39;app.wasm\u0026#39;), go.importObject).then((result) =\u0026gt; { go.run(result.instance); wireItAllUp(); // 👈 This line is new. }); Now if we save our changes (make sure to rebuild your app.wasm file after making any changes to it) and refresh the page, we should be able to finally use the form and calculate any and all square roots we could possibly want.\nAnd there you have it - Go running in the browser and doing math. The possibilities from here are essentially endless.\nHere\u0026rsquo;s a link to the complete project as well as a live version to play around with.\nWhat\u0026rsquo;s Next? # To greatly reduce the size of your Wasm binary file, consider using TinyGo as your compiler. Also, explore more real-world uses for your Wasm module, like generating images or using third-party packages for heavy processing tasks. Stay tuned and follow the blog to get updates when new posts are published.\nFurther Reading # The official Go WebAssembly docs to start diving a little deeper on the topic. The WebAssembly Docs themselves. The growing WebAssembly section of awesome-go for neat libraries and tools in the ecosystem. The list of awesome-wasm things in the community - not just using Go. go-app.dev - the up and coming package for building PWA\u0026rsquo;s (Progressive Web Apps) using Go and Wasm. ","date":"17 April 2024","externalUrl":null,"permalink":"/posts/getting-started-with-go-and-webassembly/","section":"Posts","summary":"A minimal, barebones, step-by-step guide on getting started with WebAssembly using the Go programming language.","title":"Gophers on the Web: Getting Started with Go and WebAssembly","type":"posts"},{"content":"","date":"17 April 2024","externalUrl":null,"permalink":"/tags/javascript/","section":"Tags","summary":"","title":"Javascript","type":"tags"},{"content":"","date":"17 April 2024","externalUrl":null,"permalink":"/tags/software-engineering/","section":"Tags","summary":"","title":"Software-Engineering","type":"tags"},{"content":"","date":"17 April 2024","externalUrl":null,"permalink":"/tags/wasm/","section":"Tags","summary":"","title":"Wasm","type":"tags"},{"content":"","date":"17 April 2024","externalUrl":null,"permalink":"/tags/webassembly/","section":"Tags","summary":"","title":"Webassembly","type":"tags"},{"content":"I think we can all agree that writing HTTP servers in Go is an overall simple and pleasant affair. But what happens when we find ourselves needing to read the body of an incoming request multiple times? A common use case would be a middleware that needs to read and verify the request body before it can be processed further, or another that needs to log the request body while still allowing the body to be read later on.\nThe Problem # After reading once from the io.Reader implemented by the body field of Go http requests, it becomes impossible to read from it again. There is also no builtin way to simply \u0026ldquo;reset\u0026rdquo; the body as part of the interface.\nThe simplest solution # For the simplest solution, we simply need to make sure that every time we read through the request body, we read through it fully and then replace it with a new reader. Simple.\nbody, err := io.ReadAll(r.Body) // Replace the body with a new reader after reading from the original r.Body = io.NopCloser(bytes.NewBuffer(body)) We can even make use of an io.TeeReader and bytes.Buffer.\nbuf := bytes.Buffer{} // A Buffer is both a Reader and Writer req.Body = io.TeeReader(req.Body, \u0026amp;buf) // Do some body reading, then replace the body with the buffer req.Body = \u0026amp;buf Simple Reusable Reader implementation # Because Go\u0026rsquo;s interfaces work via structural typing, we can very easily implement the io.Reader interface with a custom type as a drop-in replacement. Thus creating an io.Reader that can be \u0026ldquo;read from\u0026rdquo; an infinite number of times.\ntype reusableReader struct { io.Reader readBuf *bytes.Buffer backBuf *bytes.Buffer } func ReusableReader(r io.Reader) io.Reader { readBuf := bytes.Buffer{} readBuf.ReadFrom(r) // error handling ignored for brevity backBuf := bytes.Buffer{} return reusableReader{ io.TeeReader(\u0026amp;readBuf, \u0026amp;backBuf), \u0026amp;readBuf, \u0026amp;backBuf, } } func (r reusableReader) Read(p []byte) (int, error) { n, err := r.Reader.Read(p) if err == io.EOF { r.reset() } return n, err } func (r reusableReader) reset() { io.Copy(r.readBuf, r.backBuf) // nolint: errcheck } Basic usage # A simple example of using our reusable reader with basically any io.Reader implementation.\nfunc main() { text := \u0026#34;Lorem ipsum dolor sit amet\u0026#34; r := ReusableReader(strings.NewReader(text)) readAndPrint(r) readAndPrint(r) readAndPrint(r) } func readAndPrint(r io.Reader) { b, _ := io.ReadAll(r) fmt.Printf(\u0026#34;%s\\n\u0026#34;, string(b)) } // \u0026#34;Lorem ipsum dolor sit amet\u0026#34; // \u0026#34;Lorem ipsum dolor sit amet\u0026#34; // \u0026#34;Lorem ipsum dolor sit amet\u0026#34; Worth noting that if only dealing with strings.Readers or bytes.Readers, it is infinitely easier to simply seek back to the beginning of those readers since they implement the io.Seeker interface\nr := strings.NewReader(text) r.Seek(0, 0) // will effectively reset the reader back to the beginning Request body usage # Things get more complicated when dealing with http.Request body fields, which don\u0026rsquo;t implement the io.Seeker interface. In that case, for any http.Request we can easily use our reusable reader in place of its Body.\nfunc handler(w http.ResponseWriter, r *http.Request) { r.Body = io.NopCloser(ReusableReader(r.Body)) // Perform any reads however much we like from here on out } We needed to wrap our ReusableReader with an io.NopCloser in order to adhere to the io.ReadCloser interface. We could realistically simplify this case by implementing the io.Closer interface, just like the io.NopCloser does.\nfunc (r ReusableReader) Close() error { return nil } Which would allow the above example to be a tad simpler.\nr.Body = ReusableReader(r.Body) A more real-world example # Infinitely re-reading a static byte reader isn\u0026rsquo;t all that exciting. So let\u0026rsquo;s assume that we are building a web application and have created the following middleware for our request handlers, which both require the reading of the request body.\nfunc logRequest(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { msg := fmt.Sprintf(\u0026#34;%s %s\u0026#34;, r.Method, r.URL.Path) if body, _ := io.ReadAll(r.Body); len(body) \u0026gt; 0 { msg += fmt.Sprintf(\u0026#34; Body: %s\u0026#34;, string(body)) } log.Print(msg) next.ServeHTTP(w, r) }) } func verifyRequest(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) // Perform some request verification here if err != nil || len(body) == 0 { log.Printf(\u0026#34;%d: Request verification failed\u0026#34;, http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest) return } log.Printf(\u0026#34;Request verified: %s\u0026#34;, string(body)) next.ServeHTTP(w, r) }) } If used as is, then whichever of these middleware that we would run second would not be able to read the request body, since it would have already been read from by the first. This is where our reusable reader can come in handy, and we could simply wrap it\u0026rsquo;s logic around a third middleware that should be ran first in the chain.\nfunc reuseBody(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.Body = io.NopCloser(ReusableReader(r.Body)) next.ServeHTTP(w, r) }) } Now that we have all middleware in place, we can setup our http server and make use of the infinitely readable http.Request body.\nLet\u0026rsquo;s assume we have a handler called greet which will greet the user based on the request body payload - again needing to read from the request body after passing it through both of the other middleware already.\nfunc main() { greetHandler := http.HandlerFunc(greet) mux := http.NewServeMux() mux.Handle(\u0026#34;/greet\u0026#34;, reuseBody(logRequest(verifyRequest(greetHandler)))) fmt.Println(\u0026#34;Listening on port http://localhost:9000/\u0026#34;) log.Fatalln(http.ListenAndServe(\u0026#34;:9000\u0026#34;, mux)) } Final thoughts and drawbacks # While this is all fine and dandy with the simple examples above, it\u0026rsquo;s important to remember that this reusable reader is by nature not a very efficient implementation of the io.Reader interface; mainly because we require reading all of the data up front into a buffer. Which won\u0026rsquo;t work well for anything large like files or larger streams of data.\nAnother issue with this approach is that we are assuming that everyone attempting to read from the reader knows and assumes that it is an instance of a ReusableReader. At which point it might be better/safer for middleware to simply read from the reader, process any data and replace the reader with a new one like mentioned before.\n","date":"31 October 2022","externalUrl":null,"permalink":"/posts/reusable-go-http-request-reader/","section":"Posts","summary":"Or any other io.Reader in a reusable way","title":"Read Go HTTP Request body multiple times","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":" Hi, I\u0026rsquo;m Mike! 👋\nMichał Repeć\nSoftware Engineer Tech Lead email: mike@flexicon.dev website: https://flexicon.dev linkedin: https://www.linkedin.com/in/mikerepec/ Background # I’m a full-stack software engineer and technical leader with a decade of experience across diverse industries. Based in Silesia, Poland 🇵🇱, specializing in web technologies.\nI thrive on complex problems that actually matter to users. My toolkit spans Go, Ruby, JavaScript/TypeScript, and Kotlin - forged across roles by following the problem, not the language.\nWhen not coding, I\u0026rsquo;m usually playing video games, reading any weird-fiction book I can get my hands on, or experimenting in the kitchen with my fiancée.\nExperience # Senior Core Platform Engineer II at n8n (Apr 2026 - Present) Building out the core workflow engine for n8n, a fair-code workflow automation platform with over 400\u0026#43; integrations. Engineering Manager at ClickUp (Dec 2024 - Apr 2026) Leading two engineering squads of 5 cross-functional engineers each, responsible for building high-impact product features at scale. Staying hands-on by contributing code, reviewing pull requests, and helping drive service reliability. Staff Software Engineer at ClickUp (Aug 2024 - Dec 2024) Full-stack development within a foundational product squad. Working across Angular 18\u0026#43; frontends and Node.js backends with Nest\u0026#43;TypeScript. Senior Platform Engineer at Personio (May 2022 - Aug 2024) Providing Java libraries and frameworks as abstractions over common and critical use-cases across the organization, such as ensuring SaaS Customer Tenancy separation, Feature Flagging functionality and test wrappers around databases and AWS resources. Technology used: Kotlin, TypeScript, Go. Building dependency upgrade automations in order to reduce software staleness across an organization of more than 300 engineers. Automatic dependency upgrades for projects built with: Ruby and Kotlin. Engineering Manager at Teamwork (May 2021 - May 2022) Built and led a new team of 6 cross-functional engineers from the ground up, providing personal as well as technical leadership while maintaining a SaaS product integrations platform. Responsible for the technical architecture behind a new series of microservices built for integrating different services into an existing stack. Senior Software Engineer at Teamwork (May 2020 - May 2021) Designing and building out the software architecture for an integrations platform within a SaaS product. Maintaining Go and C# based microservices that interact via http and gRPC. Built internal Admin panel for all integrations resources at the company to simplify monitoring and management. Senior Full Stack Engineer at ComeOn Group (Apr 2019 - May 2020) Development and maintenance of a long running Payments platform, used by multiple iGaming platforms and casinos. Event driven backend with emphasis on performance and reliability, serving hundreds of thousands of transactions every week. Full Stack Engineer at Boldare (May 2018 - Apr 2019) Agile engineer working directly with clients to build bespoke enterprise frontend applications. Software Developer at Positive Power (defunct) (Jul 2017 - May 2018) PHP developer for many client projects and Frontend lead for a bespoke enterprise client application including large asset management and mailing in Angular 5. Web Developer at Apicode (Apr 2016 - Jun 2017) PHP development of client projects with a focus on Joomla and WordPress CMS platforms. Skills cloud: AWS, Docker, Kubernetes databases: PostgreSQL, MySQL, MongoDB, Redis, Apache Kafka frameworks: Angular, React, Vue, Node, NestJS, Rails languages: TypeScript, JavaScript, Go, Kotlin, Python, Ruby tools: Git, gRPC, CI/CD, Microservices ","externalUrl":null,"permalink":"/about/","section":"flexicon.dev","summary":"","title":"Michał Repeć - Software Engineer \u0026 Tech Lead","type":"page"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]