Skip to main content

    How to Install an SSL Certificate on pfSense and OPNsense

    A firewall keeps the CA, the certificate and each service's reference as separate objects. Import all three parts, or the change never reaches a client.

    MS
    My-SSL Team
    ·
    14 min read
    ·
    Published September 4, 2026
    ·
    Last updated September 4, 2026

    The short answer

    A firewall does not keep a certificate as a file. It keeps three separate things: the issuing CA, the certificate with its private key, and a reference held by each service that presents TLS. On pfSense, import every intermediate as its own entry under System > Cert. Manager on the CAs tab, import the certificate and key on the Certificates tab, then select it under System > Advanced > Admin Access and save, which restarts the web interface. OpenVPN, HAProxy and the Captive Portal each hold a reference of their own and keep serving the old certificate until they are re-pointed and restarted separately.

    A firewall holds one certificate object but several services each keep their own reference to it, so installing the certificate is only the first of three stepsA diagram in three layers. The top layer is the trust store, holding two kinds of object side by side: CA entries, which are the intermediate and root certificates the firewall uses to assemble the chain it presents, and certificate entries, each a leaf certificate paired with its private key. An arrow labelled with the note that the chain is built from CA entries, not from the bundle file, connects the two. The middle layer is a gold band labelled references. Four separate arrows leave it, one for each consumer in the bottom layer: the web interface configured under System, Advanced, Admin Access; OpenVPN servers, whose client profiles trust the CA rather than the leaf; the HAProxy package, which selects certificates of its own; and the Captive Portal, which does the same. Each consumer stores the identity of one certificate entry, and none of them is updated when a new certificate is imported. A footer states the rule the diagram exists to make visible: importing a certificate creates an object, and every reference to the old object survives that import untouched until it is changed and the service is restarted.One import, four things that still point at the old certificateTHE TRUST STORE — System > Cert. ManagerCAs tabEach intermediate, imported as itsown entry. This builds the chain.Certificates tabThe leaf plus its private key.No key here, no entry in any menu.Each service stores its own reference to one certificate entryWeb interfaceSystem > Advanced> Admin AccessRestarts on save.OpenVPNServer picks a leaf.Clients trust the CA,not the leaf.HAProxy packageIts own certificateselection, per frontend.Needs its own restart.Captive PortalSelects a certificateper portal zone.Easy to forget.Importing a certificate creates an object. It does not move a single reference.Every consumer above keeps serving the old certificate until it is re-pointed and restarted.
    The reason "I installed it and nothing changed" is such a common report: on a firewall, installing and using a certificate are two different operations.

    Why an install here is three objects, not one file

    On a web server, installing a certificate means putting files somewhere and telling one process to read them. A firewall works differently. pfSense and OPNsense both hold certificates in a configuration database, as objects with identities, and several independent services each store the identity of the object they use. Three things to get right, then, and only the middle one is what most guides call "installing".

    The consequence shows up as the most common report we see from people who have just bought a certificate for a management interface: the certificate imported cleanly, the entry is visible in the list with the right dates and the right hostname, and the browser still shows the old one. Nothing failed. The import created a new object and left every existing reference pointing where it already pointed.

    There is a second consequence, quieter and more annoying to diagnose. Because the chain the firewall presents is assembled from the CA entries it holds rather than from whatever file your certificate arrived in, a bundle that contains an intermediate does you no good until that intermediate exists as its own object. Both problems have the same root: on this platform, a certificate is a record in a database, and everything that uses it holds a pointer.

    Generate the request on the firewall, or import a key

    Decide this before you order, because it changes what you paste later. If the certificate will only ever be used by this firewall, generate the signing request on the firewall itself: the private key is created on the box, never leaves it, and the finished entry is guaranteed to have a usable key. Import an existing certificate and key only when the certificate genuinely has to be shared with another system.

    For the first path, open System > Cert. Manager, go to the Certificates tab, add an entry and choose Create a Certificate Signing Request. Fill in the common name as the hostname staff will actually type, add any additional names under the alternative-name fields, and keep the key at RSA 2048 or higher unless you have a specific reason to use an elliptic-curve key. The firewall gives you a CSR block to hand to the certificate authority.

    What you order against that request is a normal server certificate — there is no firewall-specific product. A domain-validated certificate is enough when the name is validated over DNS and nobody needs to see an organisation name in the certificate details; an OV certificate is the usual choice when an internal policy requires the company to be identified in the certificate itself. Either way you need a real, publicly resolvable hostname, because no public CA may issue for a private IP address or an internal-only name.

    A decision tree for whether to generate the certificate signing request on the firewall or import a private key created elsewhereA decision tree with one question at the top: is this certificate used only by this firewall? The yes branch, highlighted in gold, leads to generating the certificate signing request on the firewall itself, using the create a certificate signing request method in the certificate manager. The private key is created on the box and never leaves it, and the issued certificate is pasted back into the same pending request to complete it, which is why the resulting entry always has a usable key. The no branch covers a certificate shared with other systems, typically a wildcard already deployed on a web server, and leads to importing the existing certificate together with its private key. That branch carries three conditions: the key has to be an unencrypted PEM private key, a PKCS#12 or PFX container has to be converted first with OpenSSL, and the key now exists in more than one place, so every copy has to be handled accordingly. A footer notes that the signing request path is the default recommendation for a management interface, and the import path is for when a certificate genuinely has to be shared.Where should the private key be created?Is this certificate used only by this firewall?YesNoCreate the signing request hereThe key is generated on the firewall.It is never copied, mailed or stored twice.Paste the issued certificate back intothe same pending request to complete it.The entry always has a usable key.Import the certificate and keyFor a wildcard already live elsewhere.The key must be unencrypted PEM.A .pfx has to be converted first.The key now exists in two places.Track every copy you have made.For a management interface, the left path is the default. Take the right one only when sharing is the point.
    Most firewall certificates never need to exist anywhere else, and a key that was never copied is one you never have to go looking for later.

    For the second path, the one condition that catches people is the key format. The import expects an unencrypted PEM private key. A key with a passphrase, or a .pfx container exported from Windows, has to be converted first — the conversion is a single OpenSSL command, and our guide to certificate formats covers which one you need. Import the certificate without a key and the entry will exist, but it will not appear in any service menu, which is a confusing way to discover the problem.

    Importing the CA, and the chain it decides

    Do this before you import the leaf. Open System > Cert. Manager, go to the CAs tab, add an entry and choose Import an existing Certificate Authority. Paste one intermediate, give it a name you will recognise in a dropdown a year from now, and save. If your bundle contains more than one intermediate, repeat this for each one as a separate entry. Leave the private key field empty — you are not importing a CA you control.

    This is the step that decides what clients actually receive. The firewall builds the chain it presents from the CA entries it holds, so an intermediate that exists only inside the archive the certificate authority sent you is never transmitted. The certificate itself is perfectly valid. The chain is simply incomplete, and each client is left to find the missing issuer on its own.

    The chain a firewall presents is assembled from imported CA entries, so an intermediate left inside the bundle file is never sent to clientsTwo outcomes compared side by side, from the same certificate order. On the left, only the leaf certificate was imported and the intermediate was left inside the bundle file that arrived by email. The firewall presents one certificate. A desktop browser that has cached the intermediate from another site still succeeds, which is what makes the mistake so hard to spot, while a mobile client, a command line tool and an API caller each fail with an unknown or untrusted issuer error. On the right, highlighted in gold, the intermediate was imported as its own CA entry before the leaf. The firewall now presents the leaf followed by the intermediate, and every client succeeds without having to find the missing link on its own. A footer notes that both sides used exactly the same purchased certificate, and that the only difference is whether the intermediate exists as an object in the trust store.Same certificate, two chains, two very different client experiencesLeaf imported, intermediate skippedfw.example.com — sent to the clientIntermediate — still inside the .zipWhat each client doesDesktop browser — often succeeds, from cachePhone or tablet — untrusted issuercurl, openssl, scripts — verify errorReported as: "works for me"Intermediate imported as a CA entryfw.example.com — sent to the clientIntermediate — also sent to the clientWhat each client doesDesktop browser — succeedsPhone or tablet — succeedscurl, openssl, scripts — succeedReported as: nothing, which is the goalThe firewall builds the chain from CA entries, never from the bundle file you were sent.Import every intermediate in that bundle as its own CA entry, before you import the leaf.
    A desktop browser will often paper over a missing intermediate using a copy it cached elsewhere. That is exactly why the breakage lands on the people least able to explain it.

    The reason this survives testing is that desktop browsers are forgiving about it. Chrome and Edge cache intermediates they have seen on other sites and will often complete the chain without being sent it, and some clients will fetch a missing issuer over the network. The clients that will not are the ones that generate the support ticket: phones, older devices, curl, a monitoring agent, anything running in a container with a bare trust store. If the person who installed the certificate tested from their own laptop, they saw a padlock and moved on.

    You do not need to import the root. Roots are already present in client trust stores, and sending one adds a certificate to every handshake for no benefit. Intermediates only.

    Importing the certificate and its key

    With the CAs in place, go to the Certificates tab, add an entry, and choose Import an existing Certificate. Paste the leaf certificate into the certificate field and the matching private key into the key field, both including their-----BEGIN----- and -----END----- lines. If you generated the request on the firewall instead, there is no import to do: find the pending request in the same list and complete it by pasting the issued certificate into it.

    Name the entry after the certificate, not after whichever service happens to use it today. fw.example.com 2026-09 beats webgui cert. Every service menu on the box shows these names in a dropdown, you will have two entries for the same hostname during every overlap, and a name that encodes the issue date is the difference between a confident selection and a guess. This costs nothing now and saves a genuine mistake later.

    What you pasteWhere it goesIf you skip it
    Intermediate certificate(s)CAs tab, one entry eachIncomplete chain; fails on phones and command-line clients
    Leaf certificateCertificates tab, certificate fieldNothing to select anywhere
    Private key (unencrypted PEM)Certificates tab, key fieldEntry exists but never appears in a service dropdown
    Root certificateNowhere — leave it outNo effect; clients already have it

    Pointing the web interface at it

    Go to System > Advanced > Admin Access. Set the protocol to HTTPS if it is not already, then pick your new entry in the SSL/TLS Certificate dropdown and save. Saving restarts the webConfigurator, so the session drops for a moment and comes back on the new certificate. That restart is the part people skip when they change the setting and then navigate away without saving.

    If your certificate is not in that dropdown, the list is telling you something specific: it only offers certificates the firewall holds a usable private key for. An entry imported without a key, or with a passphrase-protected key, is filtered out. Go back to the Certificates tab, check whether the entry shows a private key, and re-import with the key in unencrypted PEM form if it does not.

    One habit worth adopting while you are on this page: keep a second way into the box. A mistake here — a certificate that does not match the key, a hostname nobody resolves — locks you out of the interface you would use to fix it. Console access, or SSH left enabled during the change, turns a bad afternoon into a two-minute correction.

    The services that hold their own reference

    The web interface is one consumer among several, and changing it changes nothing for the others. Each service stores the identity of one certificate entry and keeps using it. Walk the list every time you replace a certificate, because the one you forget is the one that expires in public: OpenVPN servers, IPsec, the Captive Portal, and any package such as HAProxy that selects certificates per frontend.

    OpenVPN deserves a note of its own, because it behaves better than people fear. Clients verify the server against the CA in their profile, not against one specific server certificate, so replacing the server certificate with another from the same issuer is invisible to them. What is not invisible is a change of certificate authority: the CA in every exported profile is now the wrong one, and each client refuses the connection until the profile is replaced. Treat a CA change as a client-side rollout, which is the same reasoning behind the overlap period in our guide to switching certificate authorities without downtime.

    HAProxy is the one that most often keeps serving an expired certificate after everything else has moved. It runs as a package with its own configuration, its own certificate selection per frontend, and its own restart, and none of that is touched by the Admin Access page. If your firewall publishes anything to the internet, HAProxy is probably the service your users actually reach, which makes it the last place you want to find out that a replacement was incomplete.

    Leave the old certificate entry in place until you have confirmed every consumer. Deleting an entry that something still references does not warn you at the time; it surfaces later as a service that will not start, or an interface quietly back on a self-signed certificate.

    The same job on OPNsense

    OPNsense uses the same three-object model with different menu names. Certificates and authorities live under System > Trust, split across the Authorities and Certificates pages, and the web interface certificate is chosen under System > Settings > Administration. Import each intermediate as an authority first, exactly as on pfSense, then the leaf with its key.

    The practical difference is what happens after issuance. OPNsense keeps a trust store that other services read from, and its ACME client plugin writes renewed certificates into that store, so a renewal reaches the services that reference it more cleanly than the equivalent flow on pfSense. That is a convenience rather than a substitute for checking: a service still has to be restarted to present a certificate it has just been handed.

    Everything else in this guide transfers unchanged. The chain is still built from imported authorities, a key still has to be unencrypted PEM to be usable, and every service that presents TLS still holds its own reference.

    Automating it with the ACME package

    pfSense ships an ACME package that requests, renews and installs certificates on a schedule. Install it from System > Package Manager, register an account key against your certificate authority's ACME directory, then define a certificate with the names you want and a validation method. On OPNsense the equivalent is the ACME client plugin, configured the same way.

    Choose DNS validation rather than HTTP validation on a firewall. HTTP validation needs port 80 reachable from the internet on the device whose entire job is to not have unnecessary ports open, and it cannot issue for a name that only resolves internally. DNS validation asks your DNS provider's API to publish a record instead, which works for an internal-only management name and for a wildcard, and leaves nothing listening on the WAN.

    The step that determines whether any of this works unattended is the actions list on each certificate. A renewal that writes a new certificate into the store and restarts nothing leaves you exactly where the manual process does: a valid new certificate that no service is presenting. Add the restart your services need — restarting the webConfigurator for the management interface, and whatever each other consumer requires — and confirm the package's scheduled job is enabled, because that job is what runs those actions.

    SettingWhat to choose on a firewall
    Validation methodDNS, via your provider's API. Nothing has to listen on the WAN.
    Renew After DaysA day count, not a fraction. Re-check it every time the maximum lifetime drops.
    Actions listRestart every service that presents the certificate, not only the web interface.
    Scheduled jobEnabled. Without it, nothing checks for expiry and no action ever fires.
    High-availability pairVerify the secondary independently. Do not assume a sync carried the restart across.

    The package is not limited to free certificates. Any certificate authority that exposes an ACME endpoint can be used, and a commercial account is tied to your orders through External Account Binding credentials — a key identifier and an HMAC key issued with your account. What ACME cannot automate is the organisation check in front of an OV certificate, which is a human process the first time and on each revalidation.

    What 200, then 100, then 47 days changes

    As of September 2026 a publicly trusted TLS certificate may be valid for at most 200 days, so a firewall needs a new one about twice a year. Under the CA/Browser Forum schedule in Ballot SC-081v3 that cap falls to 100 days on March 15, 2027 and to 47 days on March 15, 2029, taking the same replacement to roughly four and then eight times a year.

    Falling certificate lifetimes turn a firewall certificate replacement from an annual task into an eight-times-a-year task by 2029A timeline in four steps showing the maximum lifetime of a publicly trusted TLS certificate under CA/Browser Forum Ballot SC-081v3, and what each step means for a firewall. Before March 15, 2026 the cap was 398 days: one replacement a year, comfortably handled by hand. From March 15, 2026 it is 200 days, so two a year, still survivable as a manual runbook. From March 15, 2027 it is 100 days, roughly four a year. That step carries the highlighted boundary, because a manual import on a device nobody logs into weekly starts to get missed. From March 15, 2029 it is 47 days, roughly eight a year, where every replacement has to be unattended. Underneath, a note observes that the number of steps in each replacement does not fall as the interval shrinks, because the import, the rebinding of each service and the restarts stay the same every time.The same replacement, four times as often, then eight398 daysuntil Mar 2026once a yeara calendar note works200 daysfrom Mar 15, 2026twice a yearstill done by hand100 daysfrom Mar 15, 2027about four a yearmanual starts slipping47 daysfrom Mar 15, 2029about eight a yearunattended or missedThe interval shrinks. The number of steps in each replacement does not.Import, re-point every service, restart each one — the same work, at four then eight times the frequency.Maximum lifetimes under CA/Browser Forum Ballot SC-081v3.
    The dates are the easy part to plan around. The harder part is that a firewall is a device most teams touch deliberately, not continuously.

    The arithmetic matters more here than on a web server, because a firewall gets touched on purpose, inside a change window, and barely at all in between. A change that lands in someone's calendar once a year gets done. The same change four times a year, on a box nobody logs into between incidents, is the kind of task that slips — and when it slips on a management interface, the first symptom is usually an administrator training themselves to click past a warning.

    There is a detail in the ACME package worth revisiting each time the cap drops. Its renewal threshold is expressed as a number of remaining days, not as a proportion of the lifetime. A threshold that leaves a comfortable margin on a 200-day certificate can sit awkwardly close to, or beyond, the whole lifetime of a shorter one. Read the value your configuration actually uses; whatever it is, it was chosen for longer certificates than the ones you are about to be issued. Leave yourself enough runway to notice a failed renewal before the certificate expires. The same reasoning, applied across a whole estate, is the subject of our guide to preparing for 47-day certificates.

    Proving the change actually reached a client

    Check from somewhere that is not the firewall, and check the chain rather than the padlock. One command answers both questions at once: openssl s_client -connect fw.example.com:443 -servername fw.example.com -showcerts. Read the certificate list it prints. You want the leaf followed by the intermediate, and a verify return code of 0.

    A leaf on its own, with no intermediate beneath it, means the CA entry never got imported — that is the missing-chain problem, seen from the client side. An unexpected issuer or an old expiry date means a service is still holding a reference you have not moved yet. Repeat the command against every port that presents TLS, not just 443, because each one answers for a different service.

    For a name that resolves publicly, our SSL certificate checker does the same inspection from outside your network and reports the chain it was served, which is a useful second opinion when the answer from inside your own network looks fine. For an internal-only name, the OpenSSL command is the practical equivalent — run it from a workstation, not from a shell on the firewall.

    ClaimWhat actually proves it
    The certificate is installedThe entry appears on the Certificates tab with a private key
    The chain is complete-showcerts lists the intermediate below the leaf, from an external host
    The web interface uses itA fresh browser session on the management name, after the save-triggered restart
    Every service movedThe same check run against each TLS port the firewall answers on
    Renewal will work unattendedOne forced renewal now, watched end to end, before you rely on the schedule

    That last row is the one that separates a working setup from one that appears to work. Force a renewal on the day you configure it, while you still remember which services you re-pointed and which restart each one needs. Discovering a missing action six months later, from an expiry warning, means diagnosing a system you have entirely stopped thinking about.

    FAQ

    Frequently Asked Questions

    Get instant answers to common questions about SSL certificates and our services.

    Still Have Questions?

    Our SSL experts are available 24/7 to help with any questions about certificates, installation, or technical issues.