How to URL-Encode Special Characters in PostgreSQL Connection Strings
Encode PostgreSQL usernames and passwords safely without breaking the scheme, host, port, database name, or query parameters.
On this page
If a PostgreSQL password contains @, #, %, /, or a space, place that value in the URL only after percent-encoding the username or password component. Do not encode the complete connection URL: doing so would also encode structural characters such as :, /, @, and ? that the parser needs.
Use the PostgreSQL Connection URL Builder when you want to assemble and inspect the format field by field in your browser. Use placeholder credentials on shared devices.
PostgreSQL URL anatomy
A typical URL has this structure:
postgresql://USER:PASSWORD@HOST:PORT/DATABASE?sslmode=requireThe punctuation separates structural parts:
postgresql://is the scheme.USER:PASSWORDis the user information.@ends the user information and begins the network location.HOST:PORTidentifies the endpoint./DATABASEselects the database.?name=value&name=valuecarries connection parameters.
Those separators are URL structure. The username and password are URL components. A literal @ inside a password is data, but an unencoded @ is also meaningful syntax. Percent-encoding removes that ambiguity.
For a broader explanation of every field, read PostgreSQL Connection URL Explained, Piece by Piece.
Common encoded values
Percent-encoding represents a byte as % followed by two hexadecimal digits.
| Character | Encoded value |
|---|---|
@ | %40 |
# | %23 |
% | %25 |
/ | %2F |
| Space | %20 |
A literal percent sign must become %25. If the original password contains %40, encoding the original value produces %2540; otherwise a parser could treat %40 as an encoded @ rather than as the four literal characters %, 4, and 0.
Encode each dynamic component in JavaScript
JavaScript's encodeURIComponent is appropriate for an individual username or password:
const username = encodeURIComponent("app_user");
const password = encodeURIComponent("demo@pass#50%/with space");
const databaseUrl =
`postgresql://${username}:${password}@db.example.com:5432/app`;The result has encoded credentials while preserving the URL grammar:
postgresql://app_user:demo%40pass%2350%25%2Fwith%20space@db.example.com:5432/appApply the same rule to any other dynamic URL component. For example, encode a dynamic database name separately before interpolating it. Treat the host and port as validated configuration rather than accepting arbitrary text and trying to repair it with encoding.
Do not encode the entire URL
This is incorrect:
const broken = encodeURIComponent(
"postgresql://app_user:demo@pass@db.example.com:5432/app",
);It begins with postgresql%3A%2F%2F, so it no longer has a recognizable postgresql:// scheme. Encoding must happen before the components are joined.
Do not encode an already encoded password twice
encodeURIComponent("demo%40pass") returns demo%2540pass. That is correct only if the user's literal password contains the characters %40. If %40 was already intended to represent @, a second encoding changes the credential.
Keep the unencoded secret in a secret manager, encode it once when constructing a URL, and avoid round-tripping between encoded and decoded copies.
Space, plus, and form encoding are different contexts
encodeURIComponent("with space") produces with%20space. That is the unambiguous representation to use for a space in a PostgreSQL connection URI component.
Some web form encodings use + for a space. A URI parser does not universally apply that form rule, and PostgreSQL clients or intermediary URL libraries can differ in how they handle a literal plus. If the credential contains +, component encoding produces %2B; if it contains a space, it produces %20. Do not substitute + for %20 by hand.
Prisma DATABASE_URL example
Prisma requires special characters in PostgreSQL connection URL components to be percent-encoded. A safe placeholder value is:
DATABASE_URL="postgresql://app_user:demo%40pass%2350%25%2Fwith%20space@db.example.com:5432/example_database?schema=public&sslmode=require"The quotes above belong to dotenv syntax; they are not part of the URL value. Provider dashboards may supply an already encoded string, so inspect their documentation before transforming it again.
Errors malformed URLs can cause
Encoding failures do not always produce an explicit "bad encoding" message.
- An unencoded
@can make the parser read part of the password as the host. - An unencoded
/can prematurely begin the path containing the database name. - An unencoded
#can be treated as a fragment marker by a general URL parser, truncating the effective value. - A stray
%or invalid percent escape can cause parsing to fail. - Double encoding can make authentication fail because the decoded password no longer matches the original.
These can surface as an invalid connection string, failed authentication, an unknown host, or a reachability error. Diagnose the parsed structure before assuming PostgreSQL itself is unavailable.
Verify the format without revealing credentials
Do not print the full URL. A small Node.js check can report structure and mask user information:
const parsed = new URL(process.env.DATABASE_URL ?? "");
console.log({
protocol: parsed.protocol,
hasUsername: parsed.username.length > 0,
hasPassword: parsed.password.length > 0,
hostname: parsed.hostname,
port: parsed.port || "default",
databasePathPresent: parsed.pathname.length > 1,
sslmode: parsed.searchParams.get("sslmode"),
});Only run this where the hostname and database path are not themselves sensitive. It verifies the shape, not the correctness of the credential or the reachability of the server.
Keep connection strings out of history and logs
A connection URL is a credential. Do not place a real one directly in a shell command: shell history, process listings, CI output, application logs, and copied terminal transcripts can preserve it. Keep it in an approved secret store or environment variable, never source control.
If a real URL is exposed, rotate the database credential. Merely deleting a log line or later Git commit does not invalidate the leaked secret.
Encoding fixes only the URL grammar. It does not choose a trust policy or connection endpoint. Continue with PostgreSQL SSL Modes Explained for Node.js and Prisma, then compare a direct PostgreSQL connection with client-side and external pools.
Checklist
- Encode individual dynamic components, not the complete URL.
- Encode a literal
%as%25and a literal+as%2B. - Represent a space as
%20, not an assumed form-style+. - Avoid encoding a provider-supplied value twice.
- Validate URL structure without printing credentials.
- Keep the finished URL out of shell history, logs, and source control.
References
Documentation checked on 2026-08-08:
Related writing
- PostgreSQL Connection URL Explained, Piece by PieceA component-by-component breakdown of the PostgreSQL connection URL — user, password, host, port, database, and query parameters — plus safe environment-variable handling.
- PostgreSQL Connection Pooling: Direct Connection vs PoolerCompare direct PostgreSQL connections, application-side pools, and external poolers for persistent Node.js, serverless, Prisma, and migration workloads.
- PostgreSQL SSL Modes Explained for Node.js and PrismaUnderstand PostgreSQL sslmode choices, certificate and hostname verification, and the differences between libpq, node-postgres, Prisma, and hosted providers.