PostgreSQL Connection String Builder

Fill in the host, port, database, user and password, and this postgres connection string builder writes the URI PostgreSQL clients expect, with reserved characters in the password percent-encoded. Paste a string you already have and it travels the other way, into the fields.

postgresql://

A postgresql:// or postgres:// URI, a jdbc:postgresql:// URL, a SQLAlchemy URL, or the whole DATABASE_URL= line out of an env file. Ctrl+Enter fills the fields.

Port 6543 on the shared pooler. The user name carries the project ref, and transaction mode does not support prepared statements, which is what Prisma's pgbouncer=true is for.

Connection details

Shown in full in the result below, so it is worth hiding the field if someone is behind you.

Percent-encoded for the URI
user
postgres.abcdefghijklmnopqrstnothing to escape
password
p%40ssw0rd%231@ → %40# → %23

Only try SSL. The certificate is not checked unless a root CA file is present.

What pg_stat_activity shows for this connection.

Command-line options for the session. Spaces become %20 and = becomes %3D in the URI.

Output format
postgresql://postgres.abcdefghijklmnopqrst:p%40ssw0rd%231@aws-0-eu-central-1.pooler.supabase.com:6543/postgres?sslmode=require

The libpq URI. psql and most drivers read this form as it stands.

Everything here is computed in this tab. The page has no server to send a password to, nothing you type is stored or logged, and Ctrl or Cmd with Enter copies the string above.

What it does and how to use it

Pick the service you connect to and the fields arrive shaped the way that service documents them. Whatever you end up with is rewritten below as a psql invocation, a JDBC URL, a SQLAlchemy create_engine call, a Prisma or Drizzle DATABASE_URL, and the seedfast connect command.

Backwards works as well. Drop a string into the box at the top, press Fill the fields, and the credentials come back decoded with sslmode and options split into their own controls.

Whether you came looking for a postgres url builder or a postgres connection string generator, what comes out is the libpq URI, the one form every driver in the table further down either reads as it is or can be rewritten from.

The libpq URI format and the characters that break it

libpq documents the URI it accepts as a single line of grammar, and asks for percent-encoding wherever a part of it carries a symbol with special meaning.

postgresql://[user[:password]@][host][:port][/dbname][?param=value]

Skip the encoding and the failure is rarely subtle. A # begins the fragment and a / ends the authority, so postgresql://user:pa#ss@host/db is rejected by pg-connection-string and by the browser's own URL parser alike. Where the stray character is an @, nothing is rejected at all. The parser splits at the last one it finds, reads postgresql://user:p@sshost/db as the password p on a host called sshost, and hands you a complaint about DNS.

Everything after the ? is parameters. sslmode sets how much of the TLS handshake the client insists on, and all six values are laid out further down. Session settings ride in options, where -c search_path=app becomes ?options=-c%20search_path%3Dapp once the space and the = are escaped.

Connection strings by provider

The shapes below are copied from each provider's documentation, with the project or instance part replaced by a placeholder, so a real host you paste into the builder will run a little longer. Neon has its own page, How to Seed a Neon Database, where its -pooler host is explained. Prisma users should read Prisma Postgres Seed for the DATABASE_URL and direct URL conventions that ORM adds on top of everything here.

Supabase

Supabase hands out three strings for one database. The direct one, postgresql://postgres:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres, reaches the server itself and resolves to an IPv6 address unless the project has the IPv4 add-on. The shared pooler answers on a host such as aws-0-eu-central-1.pooler.supabase.com with the user written as postgres.[PROJECT-REF], and the port decides the mode. On 6543 the pooler runs in transaction mode, where consecutive statements can land on different backends and prepared statements are not supported, which is why Prisma's own docs add pgbouncer=true to that URL and to no other. Port 5432 on the same host is session mode, one backend held for the life of the connection. Supabase asks for sslmode=require on all of them, with verify-full and their root certificate as the stricter option.

Amazon RDS

To reach an RDS instance you copy its endpoint, a host of the form mydb.c6c8mwvfdgv0.us-west-2.rds.amazonaws.com, from the Connectivity and security tab; the port is 5432 unless you changed it, and the default master user is postgres. Since RDS for PostgreSQL 15 the rds.force_ssl parameter defaults to 1, so a client that arrives without TLS is turned away with a pg_hba.conf error that ends in SSL off, and sslmode=require is enough to pass. To have the client verify the certificate as well, download global-bundle.pem from the AWS trust store and reference it in the string, postgresql://awsuser:[PASSWORD]@mydb.c6c8mwvfdgv0.us-west-2.rds.amazonaws.com:5432/mypgdb?sslmode=verify-full&sslrootcert=global-bundle.pem. That bundle carries the root certificates for every commercial region, and AWS asks that only the roots go into your trust store, never the intermediates, because those rotate.

Heroku

Heroku never shows you a host to type. The add-on writes the whole string into the DATABASE_URL config var, shaped postgres://[USER]:[PASSWORD]@[HOST]/[DBNAME], and a failover or a credential rotation (heroku pg:credentials:rotate) rewrites that var and restarts the dynos, so any copy hard-coded elsewhere quietly stops working. Read the var at runtime and append what the client needs, which on Heroku is sslmode=require, since every connection to Heroku Postgres has to be TLS 1.2 or newer. Pasting the var into the builder is a fine way to read the parts, as long as the copy you deploy keeps coming from the config var.

Azure Database for PostgreSQL

A flexible server answers at [SERVER-NAME].postgres.database.azure.com on 5432, and the user is the plain administrator login, postgresql://[USER]:[PASSWORD]@myserver.postgres.database.azure.com:5432/postgres?sslmode=require. A login written user@myserver comes from the Single Server product Microsoft retired in March 2025, and a flexible server does not accept it. TLS is on by default through require_secure_transport, and Microsoft's guidance goes past require, asking for verify-ca or verify-full with the DigiCert Global Root G2 and Microsoft RSA Root CA 2017 certificates in your trust store and nothing below them, since the intermediates rotate without notice. The built-in PgBouncer, once enabled, sits on the same host at port 6432.

Google Cloud SQL

Rather than a host, Cloud SQL gives you an instance connection name, PROJECT:REGION:INSTANCE. From Cloud Run the instance is mounted as a Unix socket at /cloudsql/PROJECT:REGION:INSTANCE, and libpq takes a socket directory through the host parameter in the query string, because a path cannot sit in the authority part of a URI, postgresql://[USER]:[PASSWORD]@/[DBNAME]?host=/cloudsql/PROJECT:REGION:INSTANCE. Everywhere else you run the Cloud SQL Auth Proxy, ./cloud-sql-proxy --port 5432 PROJECT:REGION:INSTANCE, and connect to 127.0.0.1:5432 as though the database were local. Google's own example sets sslmode=disable on that hop and the connection is still encrypted, because the proxy carries the TLS itself. The builder needs a TCP host, so the proxy form is the one to feed it.

Railway and Render

Both platforms give a database two addresses. Railway's DATABASE_URL points at the private network, postgres.railway.internal:5432 for a service named postgres, reachable only from services in the same project and environment, while DATABASE_PUBLIC_URL goes through the TCP proxy at a host such as shuttle.proxy.rlwy.net on a port Railway assigns, which is the one to paste into a laptop client. Render draws the same line with an internal URL whose host has no region suffix and an external one of the form dpg-[ID]-a.oregon-postgres.render.com:5432. External connections to Render are always TLS and a string carrying sslmode=disable is refused, so write sslmode=require; on the internal address the certificate is self-signed, which rules out verify-ca and verify-full there.

The six sslmode values

sslmode is a client-side setting and the server never sees it. The six values form a ladder, and each rung adds one thing the client insists on before it will talk.

  • disable tries only a plain connection.
  • allow tries a plain connection first and accepts TLS if the server insists on it.
  • prefer tries TLS first and falls back to plain if the server has none. This is what you get when you write nothing.
  • require refuses anything unencrypted and checks nothing about the certificate. One catch from the libpq docs is that when a root CA file exists in the default location, require behaves like verify-ca, and relying on that is discouraged.
  • verify-ca requires TLS and checks that a CA you trust signed the server certificate.
  • verify-full does everything verify-ca does and also checks that the host name you dialled matches the certificate.

The libpq docs frame the ladder as two threats. Eavesdropping is stopped by everything from require upward, whereas a server impersonating yours is stopped only by verify-full, because verify-ca accepts any certificate the CA issued, including one for a different host, and the CA's own policy decides what that is worth.

Percent-encoding a password

A PostgreSQL connection URL with special characters in the password is the case this table exists for. The URI grammar gives @, /, ? and # structural jobs, so a password containing one of them has to be written as a percent escape, the character's byte in hex after a %. The builder does this as you type, and these are the ones that come up.

Character Write
@ %40
# %23
/ %2F
? %3F
: %3A
% %25
space %20
& %26
= %3D
+ %2B

% itself has to be escaped, otherwise 100%pass reads as a broken escape. A + stays a + in libpq even though HTML form decoding would turn it into a space, a difference that surfaces when a string passes through a web framework on its way to the driver. Where a driver takes the keyword form, host=db.example.com dbname=app password='p@ss#1', none of this applies, since there is no URI to confuse and single quotes around the value are enough.

Which driver, which format

The URI the builder writes is libpq's, and most drivers either link libpq or copy its grammar. The ones that do neither have a shape of their own.

Driver What it accepts
psql The URI or the keyword form in place of the database name, psql "postgresql://..." or psql "host=... dbname=...".
psycopg 2 and 3 The same two libpq forms as the dsn or conninfo argument, with keyword arguments overriding what the string says.
node-postgres A connectionString URI, or a config object with user, password, host, port and database.
JDBC Its own scheme, jdbc:postgresql://host:port/database, with user, password and sslmode as query parameters.
Npgsql Semicolon-separated keywords, Host=...;Username=...;Password=...;Database=...;SSL Mode=Require.
Go pgx The URI or the key=value DSN, read the way libpq reads them, environment variables included.
SQLAlchemy The URI with the driver named in the scheme, postgresql+psycopg://, and a password run through quote_plus first.

The JDBC and SQLAlchemy tabs above already rewrite the string in those two shapes, and the psql tab wraps it in double quotes so the shell leaves the ? and the & alone.

FAQ

Frequently asked questions

Because @ is the separator between the credentials and the host, and the URI parser takes the last one it sees, so part of your password turns into a host name that does not resolve. Type the password into the builder's field rather than into the string and it comes out as %40. Or hand the driver the keyword form, where password='p@ss' needs no escaping at all.

6543 suits short-lived connections from serverless functions, where transaction mode lets many callers share a few backends, at the price of prepared statements. Stay on 5432 when a connection lives for a while or when the client prepares statements, which is what Prisma's pgbouncer=true works around on the other port. The direct host only ever listens on 5432.

require is the honest choice when the certificate is self-signed or you have no root file on your side, as on Render's internal address and on most local setups. Wherever the provider publishes a root certificate, as RDS and Azure both do, verify-full is worth the extra file, because it is the only value that catches a server pretending to be yours; the certificate goes in as sslrootcert.

Yes, libpq accepts both scheme names and so does the builder, which means a Heroku-style postgres:// line and a psql-style postgresql:// one fill the same fields. JDBC is the exception, since it wants jdbc:postgresql:// and nothing else.

A JDBC URL works, along with a SQLAlchemy URL, a psql command, or the whole DATABASE_URL line out of an env file. The builder strips the wrapper and reads the URI inside it.

Not into the box at the top, which reads the URI form only and answers a host=... dbname=... string with a message saying so. Type its parts into the fields by hand and the URI comes out, ready for a client that wants that shape.

It does not. The page has no server to send it to, and closing the tab clears what you typed.