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.

Once upon a time, loading common scripts & styles from a public CDN like cdnjs or Google's Hosted Libraries was a 'best practice' - a great way to instantly speed up your page loads, optimize caching, and reduce costs.

Nowadays, it's become a recipe for security, privacy & stability problems, with near-zero benefit. Just last week, a secu...

Some Android apps go to astounding lengths to ensure that even the owner of a device can never see the content of the app's HTTPS requests.

This is problematic for security research, privacy analysis and debugging, and for control over your own device in general. It's not a purely theoretical problem either - protections like this attempt to direct...

HTTP content encoding is an incredibly powerful tool that can save you huge amounts of bandwidth and make your web or mobile application faster, basically for free.

Unfortunately, it's poorly understood by most developers. There's a lot of power here, but few people are aware of the options or what "content encoding" really means, so it's mostly le...

HTTP(S) is the glue that binds together modern architectures, passing requests between microservices and connecting web & mobile apps alike to the APIs they depend on.

What if you could embed scripts directly into that glue?

By doing so, you could:

  • Inject errors, timeouts and unusual responses to test system reliability.
  • Record & report ...

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...

Fixing DNS in Node.js

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...

Remove unused core-js dependency to shrink the package by 80+%
TechCrunch takes a look at IPFS: https://techcrunch.com/2015/10/04/why-the-internet-needs-ipfs-before-its-too-late/. It's nice to see some real coverage. Odd coming from som… https://twitter.com/i/web/status/1417429373712584718
Turns out like GitHub Actions doesn't much like it when you try to build and launch a demo Docker container, and th… https://twitter.com/i/web/status/1414880969723719697
Make Python snippets simpler, clearer & more consistent
I just ran into my first CV in the wild where somebody referenced their familiarity with HTTP Toolkit 🤯
Some *very* exciting things coming up in HTTP Toolkit soon... 😀 https://t.co/3LtdOyScWB
@puchu isEqualWith always applies recursively, so you don't lose control of child comparison by returning undefined. With the above for example, _.isEqualWith([{ a: undefined }], [{ }], comparisonFunc) returns true, even though undefined is returned when the top-level array is encountered. This seems to work for me, do you have an example where...
It's been a long road already, but it does keep getting faster and easier, and there's some very satisfying wins al… https://twitter.com/i/web/status/1412384864561676293
The 'Accept' header defines which Content-Type values a client will accept (e.g. application/json). To detect whether a client supports a Content-Encoding value, you need to look at the 'Accept-Encoding' header: developer.mozilla.org/en-US/docs/Web/HTTP/Headers/…. Regardless, Snappy isn't an officially recognized encoding...
New repo: httptoolkit/frida-android-unpinning
New repo: httptoolkit/android-ssl-pinning-demo
doc: add docs for duplex.allowHalfOpen property
Update http-toolkit from 1.4.1 to 1.4.2
Add some extra closing handshake tests
Scope server setup to a per-file before() not a global before

On the server side, you can get the IP address from the incoming request. That's the right way to do this (but see below: I think you probably don't want to do this).

In express, this is available via req.socket.remoteAddress. If you're behind some kind of reverse proxy like a CDN then this will give you the CDN's IP, not the real user, but all mod...

I suspect the main issue here is that await fetchApplications() isn't being called in your catch test, so although it does look like you are correctly mocking an Axios failure, you're not triggering the call to Axios in the fetchApplications code that you're testing.

It's worth considering whether this is worth implementing at all though, because y...

Oxford bumps racing is now livestreamed! Complete with surprisingly good commentators: https://oxfordbumpsracing.com/live.php (in… https://twitter.com/i/web/status/1403310708876484609
Big hugs to the Fastly team, but also what a great demo of why our super centralized internet architecture with may… https://twitter.com/i/web/status/1402280936373473291
Delving into webassembly against my will 😭 I want to decompress + compress brotli in the browser, and every existi… https://twitter.com/i/web/status/1395724347512328197
@cholmugod it depends. The node app there isn't sending a request, instead it's probably launching a subprocess or using an OS API to request that the system opens a browser in its preferred URL. To capture that, you'll need to intercept that, which isn't HTTP at all. Maybe you could register your script as the system browser, so it...
There's definitely points to improve, but Paddle has still saved me a mountain of pain, and I would use them over S… https://twitter.com/i/web/status/1392806809262505986
Does this actually reset the socket, or just simulate it within node? There's use cases for that but it is different from a network perspective - i.e. the other end of the socket won't receive a RST packet.