TL;DR: The compiler saw one OpenSSL and the linker is being handed another, or the SSL libraries are missing from the link line entirely. Verify with pkg-config that the header version and library version match, then link with -lssl -lcrypto after your object files. If the code uses OpenSSL 1.0-only APIs, port those calls to the 1.1 and newer API instead of downgrading the library.

## The error

```text
"undefined reference to `SSL_CTX_new'" linker errors after upgrading openssl
```

## Steps to fix

1. Check which OpenSSL the build sees: pkg-config --modversion openssl and pkg-config --cflags --libs openssl.
   Expected: One version prints consistently, for example 3.0.x, with include and lib paths that exist.

2. Confirm the symbol is in that library: nm -D on the libssl shared object from the path above, piped to grep for SSL_CTX_new.
   Expected: The symbol is listed, proving this library exports it and the mismatch is elsewhere.

3. Fix the link line: place -lssl -lcrypto after all object files and static archives (order matters for static linking).
   Expected: The undefined reference errors disappear.

4. If the headers say 1.0 but libs are 3.0 (or vice versa), point the build at one consistent install: set OPENSSL_ROOT_DIR or the pkg-config path to a single OpenSSL.
   Expected: pkg-config --modversion and the compiled-in version agree, and linking succeeds.

5. If the code calls 1.0-only APIs (SSL_library_init, SSL_load_error_strings), replace them with OPENSSL_init_ssl and the 1.1-style init calls.
   Expected: The code compiles and links against OpenSSL 1.1 or newer without deprecated calls.

## Use this when

- C or C++ link fails with undefined reference to SSL_CTX_new after an OpenSSL upgrade
- A native dependency rebuild breaks at link time naming any OpenSSL symbol
- CI upgraded the base image OpenSSL and previously green native builds now fail to link

## Not for this skill when

- Compile-time errors about missing openssl/ssl.h, which are a missing dev-headers problem
- Runtime failures loading the wrong libssl via the dynamic loader, which need rpath or loader path fixes
- Python or Node TLS errors, which are interpreter-level problems

## Variant phrasings

- undefined reference to SSL_library_init after openssl upgrade
- ld cannot find -lssl after openssl 3 upgrade
- linking against openssl 3 fails with deprecated symbol errors

## Why it happens

OpenSSL 1.1 reworked initialization and 3.0 removed or hid several legacy symbols, so headers and libraries from different major versions describe different ABIs. The compiler happily generates calls from the headers it saw, and the linker then fails because the library it was given does not export those exact symbols. Missing -lssl -lcrypto or wrong link order produces the identical message, which is why verifying the symbol in the actual library comes before editing code.

## Edge cases

- Homebrew, system, and vendored OpenSSL installs often coexist on one machine; the build may pick headers from one and libs from another silently.
- Static linking bakes the mismatch in at build time while dynamic linking defers it to runtime; check both if the binary moves between machines.
- Some projects vendor FindOpenSSL results in a CMake cache; delete the cache after changing the OpenSSL install or the stale paths persist.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_IYGc8pduRFgRxhHY3nzdFA
