Self-hosted service
alangrainger/immich-public-proxy avatar
alangrainger/immich-public-proxy

A proxy that knows nothing about your photos is the whole security model

Share your Immich photos and albums in a safe way without exposing your Immich instance to the public.

2,295 stars85 forksTypeScriptAGPL-3.0

At a glance

What is it?
immich-public-proxy sits between the public internet and a photo server that should never see the public internet, and it enforces one rule: only what Immich has already shared may pass. The readme is unusually explicit about the consequences of that rule, including the features it refuses to add, which is the mark of a project that has decided what it is.
Who is it for?
Adopt immich-public-proxy if you run Immich and want to share albums without putting your photo server on the public internet, because the alternative is exposing a share path on an instance holding every private photo you own, and this is a smaller and better-understood attack surface.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The threat model is stated in one sentence

The readme sets the problem up plainly. Immich holds all your private photos, so it is best to keep it fully locked down, and that creates a problem when you want to share a photo or a gallery with someone. The proxy is the answer, described as a barrier of security between the public and Immich that only allows through requests you have publicly shared. Then come three properties worth taking seriously, because each one removes a class of problem rather than adding a feature. It is stateless, so there is no session store, no database and nothing to lose in a restart. It needs no API key, so there is no long-lived credential sitting in the container environment waiting to be exfiltrated. And it knows nothing about your Immich instance beyond what you have shared, so its own view of your data is exactly the set of things you intended to be public. That is a small, tight security argument, and it is worth comparing with the alternative it exists to avoid. If your photo server is on the internet, it is exposed to the entire internet for the sake of a share link, and the attack surface includes everything else that instance can do, not just sharing.

The lean scope is a stated policy, not an accident

The feature requests section contains the clearest statement of what this project is. Requests are welcome, and then the author says the goal is to keep the project as lean as possible, followed by three exclusions: the proxy has read-only access to Immich, it stores nothing, and anything that needs an API key, that modifies Immich, or that would require storing a share key will not be considered. A contributing document holds the full list. Read those three constraints together and you can see why the design is what it is. Read-only means the proxy cannot be used to change your library even if it is fully compromised, which turns a worst case from data destruction into nothing at all. No storage means there is no database to protect, back up, migrate or leak, and no state to lose on restart. And refusing anything that would require storing a share key is the sharpest of the three, because a stored key is a capability: a bearer token that keeps working until it is revoked, in a system that would then have state. Every one of these refusals makes the project less useful and more trustworthy, and an author who states them in advance is telling you that the trade is intentional. If you need upload-through-the-proxy or proxy-managed password protection, this is the wrong tool and the readme has already told you so.

Four steps to install, and one that is easy to miss

The quick start is four steps and the fourth is the one that makes the difference. You download a compose file and set the two environment variables:

bash
PUBLIC_BASE_URL: https://your-proxy-url.com
IMMICH_URL: http://your-internal-immich-server:2283

Then bring the container up and check that the share healthcheck path responds, and finally go into Immich's server settings and set the external domain to the proxy's URL. After that every link Immich generates points at the proxy instead. That last step is easy to skip and its absence is the most common way this setup goes wrong, because sharing will appear to work while every link still points at your internal server. The two environment variables are the whole configuration surface at this level. One is the public base URL of the proxy, which is what it uses to build links, and the other is the URL of your Immich server, and the readme is emphatic that this one is the local, not public, address. The compose file shows a container on a single published port, running a restart-always policy, with those two variables in its environment, and a health check that curls an internal health path with a start period and a timeout. The published health path is also the verification step in the instructions, so you can confirm the container is serving before you point anything at it. Full instructions, including Kubernetes manifests, are on the documentation site rather than in the readme.

A cache rule that will cost you an afternoon if you miss it

One line in the quick start deserves its own section because it is a specific, non-obvious failure. If you use Cloudflare, the readme says to set your video share path to bypass cache, or videos may not play. The reason follows from what the proxy serves. A cache in front of a share path will hold responses for a share that its owner has since revoked or deleted, and it will hold partial video segments keyed on a URL that contains a share identifier. Serving a stale or wrong cached response from a security boundary is exactly the class of bug that gets a proxy bypassed, and a cached segment is a cached segment even after the share is gone. Bypassing the cache removes that failure and costs you the bandwidth savings you were probably expecting from putting a CDN in front. If you do want caching for images, the safe arrangement is to bypass on the video path and think carefully about the image path, and the documentation index lists downloads as a configuration topic, which suggests the readme's one line is the summary of a longer discussion. The broader point: this is a security boundary, and every layer you put in front of it, including a CDN, is a layer that can serve the wrong thing.

The container is a two-stage build with a version stamp

The container file is short and follows the modern pattern. The first stage uses the long-term support Alpine Node image, switches to a non-root user, copies only the application directory, and installs dependencies from the lockfile, then runs the TypeScript compiler twice, once for the server and once with a separate client configuration. The second stage starts from the same base, installs a command-line HTTP client so the health check works, again runs as a non-root user with the same directory ownership, copies the built application, installs production dependencies with development ones omitted, and then does three things that matter for operations. It takes a build argument for the package version, writes it into an environment variable as the application version, and sets the node environment to production. The default command runs the compiled entry point. So a running container can tell you its own version, which is the first thing anyone asks when reporting a problem with a proxy, and the build is reproducible from a lockfile with a split between server and client output. The application directory is copied on its own rather than the whole repository, so the image contains no documentation, no tests and no repository history, which is the right default for something that is publicly reachable.

Documentation, deployment options, and the mutual TLS guide

The readme is a pointer and the pointers are well chosen. The documentation is organised by task rather than by module: installation and sharing from Immich, a configuration section covering downloads, gallery layout, lightbox, metadata privacy and error responses, then three deployment guides, then troubleshooting. Two of those deployment guides tell you about topology decisions rather than settings. One covers running everything on a single domain, and one covers redirecting your root domain to a share, which is a common desire and a way to make your whole public presence a gallery. The third is the one an experienced operator should read first: a guide on securing Immich with mutual TLS. That is the right way to harden the hop between proxy and server, because the proxy authenticates to Immich and you can require the server to authenticate back, which turns a compromised network path into a failed connection rather than a stolen session. The configuration section's mention of metadata privacy is also worth a look before you share anything, since a photo's metadata can carry location history you did not intend to publish. The project is listed in a public demo gallery served from the author's own Immich instance, which is a reasonable way to see the real thing before installing, and the feature request link is the same place to propose something, with the caveat that the answer may well be no.

Editorial conclusion

Adopt immich-public-proxy if you run Immich and want to share albums without putting your photo server on the public internet, because the alternative is exposing a share path on an instance holding every private photo you own, and this is a smaller and better-understood attack surface. Do not adopt it expecting a feature-rich photo sharing product, since the author states the goal is to keep it as lean as possible and that anything needing an API key, modifying Immich, or storing a share key will not be considered. Four things to verify. That your Immich server really is unreachable from the public internet, because the proxy's security argument depends entirely on that and the quickstart says to use the local, not public, URL. That your network path between proxy and server is trusted, since the proxy holds a session with your server and a compromised proxy is a compromised route to it. That you have set Immich's external domain to the proxy, because until you do, every share link Immich generates still points at the server itself. And whether you are behind Cloudflare, where the readme says the video path needs cache bypass or videos will not play. The licence is AGPL-3.0, version 3.4.0 was released on 2026-09-21, and the last push was the same day.

Frequently asked questions

What does immich-public-proxy do?

It sits between the public internet and your Immich server and only allows through requests for content you have already shared. It is stateless, needs no API key, has read-only access to Immich, and stores nothing.

How do I install immich-public-proxy?

Download the compose file, set the proxy's public URL and your Immich server's local, not public, URL in the two environment variables, run the container up, and check that the share healthcheck path responds. Then set Immich's external domain to the proxy URL so generated links point at the proxy.

Why do my videos not play behind Cloudflare?

The readme says to set the video share path to bypass cache, or videos may not play. The reason is that a cached response can outlive the share it belongs to, which is a problem for a component whose job is to enforce what is currently shared.

Will immich-public-proxy get new features?

The author states the goal is to keep the project as lean as possible, and that anything needing an API key, modifying Immich, or requiring storing a share key will not be considered. There is read-only access and no storage, and a contributing document holds the full list of exclusions.

What licence is immich-public-proxy released under?

AGPL-3.0. Version 3.4.0 was released on 2026-09-21, following 3.3.1 and 3.3.0 in early September 2026, and the last push to the main branch was on the same day as the release.

Official sources

  1. alangrainger/immich-public-proxy on GitHub
  2. License: AGPL-3.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/alangrainger-immich-public-proxy.svg)](https://hysenlabs.com/projects/alangrainger-immich-public-proxy)