Tim Perry

Creator of HTTP Toolkit: powerful tools to debug, test & build with HTTP(S).

Passionate tech speaker, open-source contributor, and maintainer of Loglevel, Git‑Confirm and notes.

Traditionally, a TCP port has a single server listening for incoming connections, and that server expects you to send messages in the right protocol for that port. For HTTP, it's normally a web server that'll send you a response directly, or some kind of proxy that will pass all requests through to another server, and then pass the responses back.


Nothing is ever finished or perfect, and HTTP is no exception.

HTTP SEARCH is a new HTTP method, for safe requests that include a request body. It's still early & evolving, but it was recently adopted as an IETF draft standard, and it's going to add some great new tools for HTTP development everywhere.

What does that mean, why do we need a new...

CORS can be complicated. If you're struggling with it, you might discover the concept of a 'CORS proxy' that promises to solve this, like cors-anywhere or one of the many 'free CORS proxy' hosted services.

CORS proxies let you bypass the security restrictions that CORS applies, with just a tiny change of URL.

That feels convenient, but turning off ...

HTTP is used by almost all Android apps to request data, load content, and send changes to backend servers. If you can see and edit these requests & responses then you can understand, debug, and change how any app works, but Android makes this hard to do.

By default, almost all apps will use HTTPS but won't trust user-installed certificates. T...

Java and the JVM more generally are widely used for services everywhere, but often challenging to debug and manually test, particularly in complicated microservice architectures.

HTTP requests and responses are the core of interactions between these services, and with their external APIs, but they're also often invisible and inaccessible. It's hard...


HTTP is fundamental to modern development, from frontend to backend to mobile. But like any widespread mature standard, it's got some funky skeletons in the closet.

Some of these skeletons are little-known but genuinely useful features, some of them are legacy oddities relied on by billions of connections daily, and some of them really shouldn't ex...

CORS is a necessity for many APIs, but basic configurations can create a huge number of extra requests, slowing down every browser API client, and sending unnecessary traffic to your backend.

This can be a problem with a traditional API, but becomes a much larger issue with serverless platforms, where your billing is often directly tied to the numb...

DNS is one of those invisible technologies that you use every day, but which works so well that you can conveniently ignore it.

That is until it breaks completely, or slows your application down to a crawl, or you want to resolve something unusual, and then you're in a world of trouble.

Unfortunately, DNS in Node.js isn't implemented quite as you m...

Wouldn't it be neat if you could take any HTTP request URL, and immediately find the matching API documentation, along with a detailed specification of the endpoint you're talking to?

I thought that would be very neat, so I built an index to do it.

More specifically, I've taken the OpenAPI Directory - an amazing project that has collected the OpenA...

All things come to an end, even HTTP APIs. However great your API may be today, one day you'll want to release a completely new version, an improved but incompatible endpoint, a new parameter that solves the same problem better, or to shut down your API entirely. Your current API will not be live forever.

Inconveniently though, your API has clients...

If you use the command-line all day, CLI improvements can add a huge boost to your workflow. One of the simplest ways to improve things is to make your most used commands easier & faster to type, by creating aliases.

But which aliases? Which commands are most important for your usage? You can probably guess a couple, but it's hard to know for ...

When an API request doesn't work, hopefully the client receives a sensible HTTP error status, like 409 or 500, which is a good start. Unfortunately though, whilst 400 Bad Request might be enough to know who's at fault, it's rarely enough information to understand or fix the actual problem.

Many APIs will give you more details in the response body, ...

To intercept, inspect or manipulate HTTPS traffic, you need the HTTPS client to trust you.

If you want to intercept your own HTTPS on Android, perhaps to capture & rewrite traffic from your Android device for debugging or testing, how do you do that?

This isn't theoretical - HTTP Toolkit does exactly this, automatically intercepting HTTPS from...

Travis has been the most popular place to build open-source code for a long time, but the world is moving on. GitHub Actions is modern, tightly integrated with the most popular code hosting platform in the world, flexible, fast, and free (for public repos).

Travis has been popular for years though, there's still a lot of projects being built there,...

Ah, the classic 200 response + "success: false". Dear oh dear @PaddleHQ... This should clearly be a 403! https://t.co/8zyotZBvE8
I love discovering things like http://Natto.dev. Brief glimpses of totally different ways to program, somewh… https://twitter.com/i/web/status/1381947595417862149
This is an absurd masterpiece: https://github.com/zhuowei/nft_ptr
I think my latest blog post might be quite popular https://twitter.com/HttpToolkit/status/1367501410951176200 https://t.co/fT91LCcbr5

In the end, it seems this isn't possible on a normal device any way that I can find. I think is possible if you're a device admin, but that requires managed enterprise devices etc.

For now, I've handled this by watching for near-instant (less than 200ms) VPN setup failures (between running startActivityForResult(vpnIntent) and receiving onActivityR...

You do need to send a host header, but the browser actually does this for you - it automatically sets the host header to match the hostname in the URL, which is probably what you want (and that seems to be all that aws4-axios is doing in its implementation anyway).

The browser is just complaining because you're trying to override that, but you don'...

In the above code, you're sending a body with a GET request:

var options = { host: 'localhost', port: 3000, path: `/getReviewByBookId/${books[i].id}`, method: 'GET' // <-- It's a GET request }; var req = request(options, function(res) { ... }); req.write('data\n'); // <-- This writes a request body req.write('data\n'); // <-- req.end(); 


In the response, you can call res.setHeader(headerName, headerValue) to set any header, including HSTS headers. Typically you'll want to do something like:

res.setHeader('Strict-Transport-Security', 'max-age=3600; includeSubDomains'); 

That enables HSTS for 1 hour - this is a good value for a first test, but you should probably increase it to somet...

James Proxy is now unmaintained - they recommend HTTP Toolkit instead, which is also open-source & doesn't require admin privileges for install or interception.
Sometimes open-source users _can_ be nice people: https://github.com/httptoolkit/httptoolkit/issues/128
The just-announced Framework Laptop looks amazing - swappable ports, fully upgradable parts, and small & lightweigh… https://twitter.com/i/web/status/1364982997552930822
My dramatic typing has a staring role in my coworking's new ad campaign: https://www.instagram.com/p/CLWm9lLDhIo/ @betahausbcn (comp… https://twitter.com/i/web/status/1363148215047061504
Today I'm looking at Java interception support for HTTP Toolkit! Injecting proxy & cert settings into arbitrary JA… https://twitter.com/i/web/status/1362320948167335936
Great selection of product announcements today from @PaddleHQ: automatic payment rerouting to reduce failed payment… https://twitter.com/i/web/status/1361729734065983489
The bitcoin rocketship right now is tragic 🙄 There's amazing potential in crypto but proof-of-work is a ecological… https://twitter.com/i/web/status/1359155972682502150
I've been putting together a custom keyboard as a little side project, my new keycaps just arrived, and wow: https://t.co/QjtobcakT5

Your network config there is within <debug-overrides>, which only applies for debug builds. Did you build the application in debug mode, or for production? If you don't build in debug mode then that config won't apply.

You probably want to use <base-config> instead, which applies to all builds, not just debug builds. There's a full exam...

This was closed for covering more than one problem, which I think is a misunderstanding of the question. There's a couple of examples here, but they're examples of the same single problem: I want to access info about the current VPN configurations, not the active VPN network connections. I've added more detail to try & clarify that.

I have an Android VPN application. When I fire the intent to start the VPN (via VPNService.prepare), it fails immediately if there's an always-on VPN already configured on the device. That seems reasonable, but I'd like to be able to easily detect that case, so I can show a helpful message to the user.

By 'always on' I mean the specific VPN always-...

This looks exciting - I can imagine some great multi-threading APIs that this could bring to JS. Not to mention in… https://twitter.com/i/web/status/1353979907932647424
First blog post of 2021! I'm going to try to get back into the one-a-week habit from here on. Next week: how to ch… https://twitter.com/i/web/status/1352267217514147847
"I honestly think your toolkit is one of the few opensource projects that actually works" (My new favourite user f… https://twitter.com/i/web/status/1348994660014952449
Starting 2021 off right: I'm moving all my work email into @missiveapp. Snooze! Private inline notes! Fine-tuned n… https://twitter.com/i/web/status/1347583972763721733