gohugoio/hugo · error
failed to verify certificate
Error message
failed to verify certificate: %v
What it means
`verifyCert` runs Go's `x509.Certificate.Verify` against the root pool and the requested hostname (`DNSName`). This wraps the verification failure, meaning the cert does not chain to the trusted root and/or the hostname/SAN does not match. `%v` is Go's verify error.
Solutions
- Delete the cached `_mkcerts/<hostname>.pem` and key so Hugo reissues for the current hostname.
- Ensure `$CAROOT` points at the same root CA that signed the site cert.
- Confirm the requested hostname matches a SAN in the cert (use `openssl x509 -text`).
- Re-run `mkcert -install` to make sure the root is trusted system-wide.
Example fix
// before // baseURL changed to https://myapp.test/ but cached cert is for localhost // after // rm -rf $cachedir/_mkcerts && hugo server --tlsAuto --baseURL=https://myapp.test/
Defensive patterns
Strategy: validation
Validate before calling
import "crypto/x509"
func validateCertForHost(certPEM, rootPEM []byte, host string) error {
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM(rootPEM) { return fmt.Errorf("bad root") }
block, _ := pem.Decode(certPEM)
if block == nil { return fmt.Errorf("bad cert PEM") }
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil { return err }
opts := x509.VerifyOptions{DNSName: host, Roots: roots}
_, err = cert.Verify(opts)
return err
} Type guard
null
Try / catch
// If verify fails after a hostname change, reissue for the new host.
if _, err := cert.Verify(opts); err != nil {
os.Remove(certFile); os.Remove(keyFile)
// next start provisions certs for the current hostname
} Prevention
- When the server hostname changes, delete the `_mkcerts` cache so certs reissue.
- Keep `$CAROOT` consistent with the CA that signed the site certs.
- Verify cert SANs cover every hostname you serve.
When it happens
Trigger: Fires when the cached site cert was issued for a different hostname than the current `baseURL` hostname, the root CA differs from the one that signed it, or the cert has expired.
Common situations: Changing the server `baseURL`/hostname after generating certs (cert was for `localhost`, now serving `mysite.test`); a regenerated mkcert root CA invalidating older site certs; an expired cached cert.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to parse certificate
- failed to parse certificate PEM
- failed to parse root certificate
- --appendPort=false not supported when in multihost mode
- cannot use --renderToMemory with --destination
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/045aabd313b0ca87.
Report an issue: GitHub.
Appendix: source
Thrown at commands/server.go:747
return fmt.Errorf("failed to parse root certificate")
}
block, _ := pem.Decode(certPEM)
if block == nil {
return fmt.Errorf("failed to parse certificate PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse certificate: %v", err.Error())
}
opts := x509.VerifyOptions{
DNSName: name,
Roots: roots,
}
if _, err := cert.Verify(opts); err != nil {
return fmt.Errorf("failed to verify certificate: %v", err.Error())
}
return nil
}
func (c *serverCommand) createServerPorts(cd *simplecobra.Commandeer) error {
flags := cd.CobraCommand.Flags()
var cerr error
c.withConf(func(conf *commonConfig) {
isMultihost := conf.configs.IsMultihost
c.serverPorts = make([]serverPortListener, 1)
if isMultihost {
if !c.serverAppend {
cerr = errors.New("--appendPort=false not supported when in multihost mode")
return
}
c.serverPorts = make([]serverPortListener, len(conf.configs.Languages))
}View on GitHub (pinned to 52c9bd7908)