chatgpt-web: a permanent fork that exists to add the database the upstream refused
A third-party ChatGPT Web UI page built with Express and Vue3, through the official OpenAI completion API. / 用 Express 和 Vue3 搭建的第三方 ChatGPT 前端页面, 基于 OpenAI 官方 completion API.
At a glance
- What is it?
- This project is a self hosted ChatGPT front end, and the reason it is a separate repository at all is a single disagreement: the original author would not take on a database dependency. Everything the fork adds, user accounts, session sync, quota limits, redemption codes, an admin panel and single sign-on, follows from having somewhere to put state.
- Who is it for?
- chatgpt-web is the right pick if you want to hand a team access to a paid model through your own front end, with your own key, your own accounts and a quota that stops one person spending the month budget, and you are willing to run a database and secure a public endpoint. It is the wrong pick if you want a single user interface on one machine, because the entire feature set is downstream of a database you would otherwise not need.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 7 months ago.
- What is it written in?
- Mainly Vue, 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
One refusal created this repository
The most important fact about this project is in a callout at the top of its readme, and it is not a feature. This is a permanent fork of an earlier ChatGPT web front end, and the stated reason for the split is that the original author did not want to introduce a database dependency. A link to the discussion is provided, along with thanks to the original author.
That single architectural disagreement explains almost everything else about the project, so it is worth tracing the consequences before looking at any feature.
The upstream design implied no server-side state that mattered. Configuration lived in an environment file, conversations lived wherever the client put them, and there was no notion of a user. A front end like that is genuinely simpler to run, and the argument for refusing the database is a good one: every deployment becomes a file copy rather than a migration, and there is no account system to breach.
A fork that takes the opposite position inherits the obligation to own all of that state properly. The moment there is a database, there is a schema, there are users, there is something to migrate when it changes, and there is a set of decisions about who may do what. The feature list of this project is not a random collection of additions. It reads as the complete set of things a multi user deployment needs once somebody has agreed to store state, which is why a project that presents itself as a chat interface is really a small identity and quota system with a chat interface attached.
The project is explicit that the model access is through the official API rather than through a scraped web session, and that an API key is required. That decision shapes everything downstream, because an official key is metered per token, which is what makes quotas and rate limits a feature rather than a nicety.
What the database bought, feature by feature
The additions are listed as a flat set of checkmarks, which flattens a causal chain into a shopping list. Reordering them by what depends on what makes the design legible.
The foundation is user management: registration, login, password reset and two factor authentication. Nothing else in the list is possible without it. Next comes synchronisation of conversation history, which is the first feature that is genuinely impossible without a server store, since the whole point is that a user sees the same conversations from another machine. Next is the ability to set the API key from the front end page rather than the environment file, which is what makes a shared deployment possible, since an administrator and an end user no longer have to share a configuration file.
Then come the controls that only matter in a multi user context. Conversation count limits exist per user, with an administrator able to set different limits for different people. Redemption counts extend that idea into a mechanism: a user is granted a number of uses rather than a flat allowance, which is how you distribute access in an organisation without maintaining a list. A user management panel and a key manager are the administrative surface over all of that.
Two more sit alongside. Custom sensitive words give a deployer the ability to filter or block terms in a deployment that has to comply with local policy, which is a requirement in some jurisdictions rather than a preference. Per-conversation custom prompts let a user pin instructions to one thread, which is a small feature that only makes sense once a user can have many threads stored server side.
Read in that order, the project is coherent: identity, then persistence, then metering, then administration. The final entries extend it outward rather than deepening it. Single sign-on is done by delegating authentication to a reverse proxy that speaks directory protocols, which is a common pattern in corporate deployments and means this project does not implement identity federation itself. Web search is an integration with a third party search API rather than a built-in capability. Support for a self hosted model server, an option to turn off extended reasoning, and a control over the context window are model-routing features for operators who are not using the paid endpoint.
The rate limit and proxy variables are the real configuration surface
The environment variable list is short, and three of its entries do more work than the rest.
One is an access key. The documentation is blunt about it: if you publish the project to a public network you should set this variable to add password protection, and you should also change the title in the page's HTML so the site is not findable by a keyword search. Both warnings appear in the introduction, before any configuration instructions, which is the right place for them. The reasoning is not hard to see. This is a metered endpoint. An unprotected deployment is an open relay that someone else will use, and the bill lands on whoever supplied the key.
The second is a per hour request cap, which is optional and unlimited when unset. This is the mechanism that turns a single key into a shared resource without turning it into a liability, and the fact that it is expressed as a request count rather than a token count is a reasonable simplification for a front end whose users are having conversations rather than running a pipeline.
The third group is network egress. There is support for a SOCKS proxy, where the host and port must be set together, with optional username and password credentials, and separately a standard proxy variable that accepts three schemes. The pair of SOCKS variables each note that they only take effect when both are present, which is a small correctness detail that many tools get wrong. The installation itself is two commands in two directories, one in the service folder and one at the root:
pnpm install
pnpm bootstrapThe readme also contains a warning about network reachability in some regions, telling users that if the official endpoint is unreachable they need their own proxy and must never use a public one belonging to a stranger. That advice is correct and worth repeating here for a different reason: a proxy in this position sees every request including the API key in its header, so a shared proxy is a credential disclosure, and the project's own documentation treats it as a security problem rather than a convenience.
Everything else in the configuration is a base URL override, a timeout, a switch to silence debug logging, and a model override for the container. The base URL override is what makes the self hosted model support possible, since pointing it at a local server is the same mechanism as pointing it at the official endpoint.
Bootstrap ordering, and a password salt named after its algorithm
The compose file and the documented environment variables together describe a deployment that cannot come up in an arbitrary order, and the readme handles that with comments rather than with a migration tool.
The first ordering constraint is the database URL. The access key variable doubles as the switch that enables login, and enabling login requires the database to be configured. So the sequence is: set the database connection, set the access key, start the service, register the administrator, then configure the rest from the admin page. The documentation makes the last step explicit, noting that additional configuration is set after running and registering an administrator.
The second ordering constraint is registration. The registration switch has a comment that is more useful than it first appears: it must be turned on, otherwise not even the administrator can register, and it can be turned off later. That is a bootstrap pattern, and it is worth recognising as a security decision rather than a convenience. A registration switch that is on by default and off after setup is the right default, and the failure mode it produces if you get the order wrong is a service you cannot log into, which is a much better failure than one that anyone can log into.
The third variable is the one to think hardest about. It is named as a salt for password encryption, and the name includes the algorithm. Whatever is being computed, it is not a modern password hash, and an MD5 based scheme with a shared salt is fast enough to brute force at scale on commodity hardware. Any deployer who inherits this variable from a template is inheriting a password storage scheme that predates current guidance by a long way.
The mitigation is available to the deployer rather than the project. Put this deployment behind whatever identity provider your organisation already uses, using the single sign-on path through an authentication proxy, and the local password store stops mattering. That is a better answer than trying to fix the salt, and it is the arrangement the project already supports.
A deployment posture built to avoid being indexed
Two things in the documentation point the same direction, and neither is about features.
The first is the advice to change the page title before publishing, given as a way to prevent the site being found through a keyword search. The second is a section documenting how to block crawlers at the reverse proxy, with a complete configuration snippet: a user agent match against a list containing a dozen named crawlers plus generic patterns, returning a refusal status. The snippet lives in the compose directory as a reference file, so it is meant to be copied into a proxy configuration rather than applied by the application.
Together these describe a project that expects to be deployed privately and would rather not be found. That is a coherent position for a service where one party holds the API key and everyone else spends from it. A publicly indexed chat interface with a metered key behind it is both a cost exposure and, in some jurisdictions, a data protection question, and the author is being consistent about that.
The crawler list itself is worth a note. It blocks the major search engines, which is unusual for a web application and confirms the intent, and it also blocks ordinary command line tools, which has a side effect: a health check or a monitoring probe that identifies itself as a command line client will be refused. Anyone who wires this behind a load balancer with an automated check should confirm the probe passes before assuming the deployment is broken.
The deployment targets named alongside this are a container registry, a one click platform template, and a directory of orchestration manifests, so the project expects to be run on a real host rather than on a laptop. There is also a note that changing an environment variable on that platform triggers a fresh deployment, which is a useful thing to know before you set a variable in a hurry and watch your service restart.
Two run commands, two very different network postures
The container instructions show the same image run twice, and the difference between the two commands is the most consequential line in the deployment section.
The first runs in the foreground and publishes the port on all interfaces, which is what you want when you are connecting to it from another machine on a network and what you do not want on a host with a public address. The second runs detached, restarts with a policy, and binds the port to the loopback address only. Same image, same environment, same port number, and the second one is invisible from outside the host.
That is a well chosen default pair. The detached form is the one a reader is more likely to copy, and it is the safer of the two, while the foreground form exists for the case where you genuinely need to reach it. What is missing is a proxy in front of either, and the documentation does not push one, which is why the crawler snippet is supplied as something to paste into your own configuration. The application is expected to sit behind something you control.
The compose file adds the database as a second service with its own credentials, a named volume for the data directory, a timezone pinned to a specific region, and the request cap set to a value meaning unlimited. The port for the database is both published and exposed, which is a detail to look at twice: publishing it maps it to the host, and in a single host deployment that is a reasonable convenience, but it is also an unauthenticated-looking path to your data unless the credentials in the file are strong and rotated. The compose file also carries a placeholder password in the example, so copying it as-is is the risk, not the port mapping.
One other line is worth quoting because it is the kind of thing that saves an afternoon. The image is always pulled at the latest tag, so updating means re-pulling the tag rather than watching for a version bump. That is convenient for a small personal deployment and unsuitable for one where you need to know what version is running.
What a long lived fork leaves behind
Two small details in the repository are worth a reader's attention, because they are the kind of thing that only shows up in a project that has been running for a while.
The first is a version mismatch. The release history shows a 3.x series with the most recent tag in March 2026, and the JavaScript manifest in the repository declares a 2.x version with the digits otherwise transposed. The two numbers look like the same number typed twice, which suggests a typo rather than a deliberate scheme, and it means the authoritative version lives in the release tags and in the build arguments rather than in the manifest. The container build takes the commit hash and the branch name as explicit build arguments, which is the right way to stamp a build and also means the manifest field is not what the running service reports.
The second is a file in the repository root named as an environment file, sitting alongside the documentation that instructs you to copy a template environment file into the service directory. A committed environment file is normal in some workflows and a problem in others, depending entirely on what is in it. Since the file that ships is the one the project itself uses to run, it is worth opening before you deploy anything, and the presence of a separate ignore file suggests the author is aware of the distinction.
The third is a layout decision, and it is a good one. The project deliberately does not use a workspace layout, and the reason given is that it reduces the burden on someone who only wants to work on the back end. The consequence is stated too: if you only want the front end you can delete the service folder. For a project whose front end and back end are genuinely separable, which this one is, that is the right trade. A user who wants the interface and nothing else does not have to understand a monorepo, and the dependency commands are split accordingly, with one install in the service directory and a bootstrap command at the root.
Also present, and worth noting as a sign of a maintained project rather than a legacy one: a commit message convention enforced by tooling, a hook installer wired into the bootstrap command, an editor configuration for the root manifest, agent instructions committed at the top level, and a type check that runs as part of the build rather than beside it.
Editorial conclusion
chatgpt-web is the right pick if you want to hand a team access to a paid model through your own front end, with your own key, your own accounts and a quota that stops one person spending the month budget, and you are willing to run a database and secure a public endpoint. It is the wrong pick if you want a single user interface on one machine, because the entire feature set is downstream of a database you would otherwise not need. Before exposing it, read the two environment variables that gate login and registration, since without them the admin account cannot be created at all, and reconsider the password salt, whose name declares the algorithm it uses. If you deploy it publicly, do the two things the readme tells you to do and set the access key and change the page title, because a metered endpoint without a key is an invitation.
Frequently asked questions
Why does chatgpt-web exist as a separate repository from the original project?
It is a permanent fork, and the stated reason is that the original author was unwilling to introduce a database dependency. Taking on a database is what makes user accounts, session synchronisation, quota limits, redemption codes and single sign-on possible in this version.
What must I set before the first run of chatgpt-web?
The model API key is required, plus a database connection string. The access key variable must be set to enable login, registration must be switched on so the administrator account can be created, and the database connection must exist before login is enabled. Further configuration is set from the admin page after the first registration.
What does MAX_REQUEST_PER_HOUR do in chatgpt-web?
It caps how many requests an hour the service will handle, and it is optional and unlimited when unset. It is the mechanism that lets one metered model key be shared by several people without any single user consuming the whole allowance.
Does chatgpt-web support single sign-on and which protocols?
Yes, through an authentication proxy placed in front of the application, which can speak directory protocols including LDAP, OIDC and SAML. The project delegates authentication to that proxy rather than implementing federation itself.
How does chatgpt-web handle web search?
It integrates a third party search API to provide live network search within a conversation. The feature is an integration rather than a built-in capability, so it depends on an external service and its configuration.
Official sources
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.
[](https://hysenlabs.com/projects/chatgpt-web-dev-chatgpt-web)