FIPS_MODULE(7SSL)

🌸 fips_module β€” your OpenSSL FIPS module guide, bestie 🌸

okay so real talk, the manual name is FIPS_MODULE(7SSL) and this is the guide, and it's genuinely iconic. grab a snack. πŸ“


✨ NAME ✨

fips_module β€” the OpenSSL fips module guide, no cap πŸ’–

πŸ¦‹ SYNOPSIS πŸ¦‹

Just go read the individual manual pages for the deets, babe. That's it, that's the synopsis.

See the individual manual pages for details.

πŸ‘‘ DESCRIPTION πŸ‘‘

So this guide is literally a whole tour of the different ways you can use OpenSSL together with the FIPS module. Which approach is the correct approach? Depends on you, your situation, and what you're actually trying to achieve. main character energy, but everyone's main character is different. πŸ’…

If you're here for install info specifically, go peep https://github.com/openssl/openssl/blob/master/README-FIPS.md.

⚠️ Heads up: the old functions FIPS_mode() and FIPS_mode_set()? Gone. Not here. So if your app is still calling them you must remove them. Not optional, bestie.

Apps written to use the OpenSSL 3.0 FIPS module should not touch any legacy APIs or features that dodge the FIPS module. Specifically, that means:

  • Low level cryptographic APIs β€” use the high level APIs instead, like EVP. ✨
  • Any functions that create or modify custom "METHODS" β€” for example RSA_meth_new(), EC_KEY_METHOD_new(), etc. πŸ™ˆ

All of those APIs are deprecated in OpenSSL 3.0, so a super simple rule for you: just avoid every deprecated function. Periodt. Wanna see the full list of deprecated functions? Check ossl-guide-migration(7).


🌈 Applications running on systems operating in FIPS-approved mode 🌈

When you're running on systems that are operating in FIPS-approved mode and nothing is loading providers explicitly (neither via the app nor via config), the FIPS module gets loaded implicitly as a fallback provider the very first time an algorithm is fetched. And the "default" provider? Not loaded implicitly in this case, ngl β€” you have to load it explicitly if your app needs it.

Every OSSL_LIB_CTX(3) is created with "fips=yes" as a default property in this mode, so algorithm fetches will only pull algorithms that define this property and are FIPS-approved. Wanna use algorithms that aren't FIPS approved? Two ways, bestie:

  • Specify "-fips" or "fips=no" as a property query string when you call fetching functions, or
  • Use the EVP_default_properties_enable_fips(3) function to remove the default property for a specific OSSL_LIB_CTX.

πŸŽ€ Making all applications use the FIPS module by default πŸŽ€

One simple approach: make all applications using OpenSSL use only the FIPS module for cryptographic algorithms, by default. Slay.

This one's pure configuration, no code. As long as apps are built and linked against OpenSSL 3.0 and don't override the loading of the default config file or its settings, they'll automatically start using the FIPS module with zero further code changes. Obsessed.

To do it, you gotta modify the default OpenSSL config file. Where it lives depends on your platform and whatever options were passed during the build process. Check the location by running this:

$ openssl version -d
OPENSSLDIR: "/usr/local/ssl"
⚠️ Caution: so many Operating Systems install OpenSSL by default, and it's a super common error to not have the correct version of OpenSSL in your $PATH. Double check you're running an OpenSSL 3.0 version like this:
$ openssl version -v
OpenSSL 3.0.0-dev xx XXX xxxx (Library: OpenSSL 3.0.0-dev xx XXX xxxx)

That OPENSSLDIR value up there gives you the directory where the default config file lives. So in this case, the default config file is called /usr/local/ssl/openssl.cnf.

Now edit the config file and add these lines near the beginning:

config_diagnostics = 1
openssl_conf = openssl_init

.include /usr/local/ssl/fipsmodule.cnf

[openssl_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
fips = fips_sect
base = base_sect

[base_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes

Obviously that include file location has to match the path and name of the FIPS module config file you installed earlier β€” see https://github.com/openssl/openssl/blob/master/README-FIPS.md.

For FIPS usage, turning on the config_diagnostics option is recommended, because it prevents accidental use of non-FIPS validated algorithms via broken or mistaken configuration. See config(5).

Any applications that use OpenSSL 3.0 and get started after you make these changes will use only the FIPS module, unless those applications take explicit steps to avoid this default behaviour. Also note: this configuration activates the "base" provider too. The base provider has no cryptographic algorithms in it (so it doesn't impact the validation status of any cryptographic operations, don't worry πŸ’œ), but it does include other supporting algorithms that might be required. It's designed to be used in conjunction with the FIPS module.

The big glow-up advantage here: it's simple, and zero code changes are needed in your applications to benefit from the FIPS module. But bestie, there are some disadvantages:

  • You may not want all applications to use the FIPS module.

It might be that some applications should and some should not use the FIPS module.

  • If applications take explicit steps to not load the default config file, or to set different settings.

Then this method will not work for those cases.

  • The algorithms available in the FIPS module are a subset of the algorithms available in the default OpenSSL Provider.

If any applications try to use algorithms that aren't present, they will fail.

  • Usage of certain deprecated APIs avoids the use of the FIPS module.

If any applications use those APIs, then the FIPS module will not be used.


πŸ’œ Selectively making applications use the FIPS module by default πŸ’œ

A lil variation on the above: do the exact same thing, but on an individual application basis. The default OpenSSL config file depends on the compiled in value for OPENSSLDIR, as described in the section above. BUT you can also override which config file gets used via the OPENSSL_CONF environment variable. Like this β€” on Unix, this executes the application with a non-standard config file location:

$ OPENSSL_CONF=/my/nondefault/openssl.cnf myapplication

Using this mechanism you control which config file is loaded (and therefore whether the FIPS module is loaded) on an application by application basis. Gorgeous. 😌

This removes that disadvantage up there about maybe not wanting all applications to use the FIPS module. All the other advantages and disadvantages still apply though. Fairs.


🫢 Programmatically loading the FIPS module (default library context) 🫢

Applications may choose to load the FIPS provider explicitly instead of letting config do it. The config file is still necessary though β€” it holds the FIPS module config data (like its self test status and integrity data). But in this case we don't automatically activate the FIPS provider via that config file.

To do it this way: configure as per "Making all applications use the FIPS module by default" above, but edit the fipsmodule.cnf file to remove or comment out the line that says "activate = 1".

⚠️ note that setting this value to 0 is not sufficient, bestie. Remove it or comment it out. for real.

This means all the required config info is available to load the FIPS module, but it's not automatically loaded when the application starts. The FIPS provider can then be loaded programmatically like this:

#include <openssl/provider.h>

int main(void)
{
    OSSL_PROVIDER *fips;
    OSSL_PROVIDER *base;

    fips = OSSL_PROVIDER_load(NULL, "fips");
    if (fips == NULL) {
        printf("Failed to load FIPS provider\n");
        exit(EXIT_FAILURE);
    }
    base = OSSL_PROVIDER_load(NULL, "base");
    if (base == NULL) {
        OSSL_PROVIDER_unload(fips);
        printf("Failed to load base provider\n");
        exit(EXIT_FAILURE);
    }

    /* Rest of application */

    OSSL_PROVIDER_unload(base);
    OSSL_PROVIDER_unload(fips);
    exit(EXIT_SUCCESS);
}

Note that this should be one of the first things you do in your application. If any OpenSSL functions get called that require cryptographic functions before this happens, and no provider has been loaded yet, then the default provider gets automatically loaded. And if you then later explicitly load the FIPS provider, you'll have both the FIPS and the default provider loaded at the same time. And if multiple implementations are available and you haven't explicitly specified via a property query (see below) which one should be used β€” it is unspecified which implementation of an algorithm will be used. πŸ’€

Also note: in this example we've additionally loaded the "base" provider. That loads a sub-set of algorithms that are also available in the default provider β€” specifically the non cryptographic ones, which may be used in conjunction with the FIPS provider. For example it contains algorithms for encoding and decoding keys. If you decide not to load the default provider, you'll usually want to load the base provider instead.

In this example we're using the "default" library context. OpenSSL functions operate within the scope of a library context. If no library context is explicitly specified, the default library context is used. More deets about library contexts are in the OSSL_LIB_CTX(3) man page. 🌟


🧚 Loading the FIPS module at the same time as other providers 🧚

You can totally have the FIPS provider and other providers (like the default provider) all loaded at the same time into the same library context. Then you use a property query string during algorithm fetches to say which implementation you'd like. Chef's kiss.

For example, to fetch an implementation of SHA256 that conforms to FIPS standards, specify the property query "fips=yes" like this:

EVP_MD *sha256;

sha256 = EVP_MD_fetch(NULL, "SHA2-256", "fips=yes");

If no property query is specified, or if more than one implementation matches the property query, then it is unspecified which implementation of a particular algorithm will be returned. πŸ™ƒ

This next example shows an explicit request for an implementation of SHA256 from the default provider:

EVP_MD *sha256;

sha256 = EVP_MD_fetch(NULL, "SHA2-256", "provider=default");

And you can also set a default property query string. The following example sets the default property query of "fips=yes" for all fetches within the default library context:

EVP_set_default_properties(NULL, "fips=yes");

okay so bestie, picture this πŸ’–: your fetch function has BOTH an explicit property query specified AND a default property query defined. What happens?? The two queries get merged together and both of them apply, obviously. And if the same property name is specified in both? The local property query overrides the default properties. local wins, always, no cap. πŸ’…


There are two important built-in properties you should be aware of, and honestly they're kind of the main characters here πŸ‘‘:

  • "provider" β€” this one lets you specify which provider you want an implementation to be fetched from, e.g. "provider=default" or "provider=fips" ✨. Every algorithm implemented in a provider has this property set on it. every single one.
  • "fips" β€” all FIPS algorithms match against the property query "fips=yes". And plot twist πŸŽ€: there are also some non-cryptographic algorithms available in the default and base providers that ALSO have the "fips=yes" property defined for them. These are the encoder and decoder algorithms that can (for example) be used to write out a key generated in the FIPS provider to a file. The encoder and decoder algorithms are not in the FIPS module itself, but they're allowed to be used in conjunction with the FIPS algorithms. it's giving "technically outside the boundary but we're chill about it" πŸ¦‹

You can specify default properties within a config file too, bestie 🫢. For example, the following config file automatically loads the default and FIPS providers and sets the default property value to be fips=yes. Note that this config file does not load the "base" provider. Why? Because all supporting algorithms that are in "base" are also in "default", so it's unnecessary in this case:

config_diagnostics = 1
openssl_conf = openssl_init

.include /usr/local/ssl/fipsmodule.cnf

[openssl_init]
providers = provider_sect
alg_section = algorithm_sect

[provider_sect]
fips = fips_sect
default = default_sect

[default_sect]
activate = 1

[algorithm_sect]
default_properties = fips=yes

🌈 Programmatically loading the FIPS module (nondefault library context) 🌈

So besides using properties to separate usage of the FIPS module from other usages, you can ALSO do it with library contexts 🧚. In this example we create two library contexts. In one, we assume the existence of a config file called openssl-fips.cnf that automatically loads and configures the FIPS and base providers. The other library context will just use the default provider. iconic behaviour honestly:

OSSL_LIB_CTX *fips_libctx, *nonfips_libctx;
OSSL_PROVIDER *defctxnull = NULL;
EVP_MD *fipssha256 = NULL, *nonfipssha256 = NULL;
int ret = 1;

/*
 * Create two nondefault library contexts. One for fips usage and
 * one for non-fips usage
 */
fips_libctx = OSSL_LIB_CTX_new();
nonfips_libctx = OSSL_LIB_CTX_new();
if (fips_libctx == NULL || nonfips_libctx == NULL)
    goto err;

/* Prevent anything from using the default library context */
defctxnull = OSSL_PROVIDER_load(NULL, "null");

/*
 * Load config file for the FIPS library context. We assume that
 * this config file will automatically activate the FIPS and base
 * providers so we don't need to explicitly load them here.
 */
if (!OSSL_LIB_CTX_load_config(fips_libctx, "openssl-fips.cnf"))
    goto err;

/*
 * Set the default property query on the FIPS library context to
 * ensure that only FIPS algorithms can be used.  There are a few non-FIPS
 * approved algorithms in the FIPS provider for backward compatibility reasons.
 */
if (!EVP_set_default_properties(fips_libctx, "fips=yes"))
    goto err;

/*
 * We don't need to do anything special to load the default
 * provider into nonfips_libctx. This happens automatically if no
 * other providers are loaded.
 * Because we don't call OSSL_LIB_CTX_load_config() explicitly for
 * nonfips_libctx it will just use the default config file.
 */

/* As an example get some digests */

/* Get a FIPS validated digest */
fipssha256 = EVP_MD_fetch(fips_libctx, "SHA2-256", NULL);
if (fipssha256 == NULL)
    goto err;

/* Get a non-FIPS validated digest */
nonfipssha256 = EVP_MD_fetch(nonfips_libctx, "SHA2-256", NULL);
if (nonfipssha256 == NULL)
    goto err;

/* Use the digests */

printf("Success\n");
ret = 0;

err:
EVP_MD_free(fipssha256);
EVP_MD_free(nonfipssha256);
OSSL_LIB_CTX_free(fips_libctx);
OSSL_LIB_CTX_free(nonfips_libctx);
OSSL_PROVIDER_unload(defctxnull);

return ret;

Note that we made use of the special "null" provider here, which we load into the default library context πŸ‘€. We could have chosen to use the default library context for FIPS usage and just created one additional library context for other usages β€” or vice versa. Whatever vibes. But here's the thing: if code has not been converted to use library contexts, then the default library context will be automatically used. This could be the case for your own existing applications as well as certain parts of OpenSSL itself, because not all parts of OpenSSL are library context aware 😭. If that happens, you could "accidentally" use the wrong library context for a particular operation. To be sure this doesn't happen, you can load the "null" provider into the default library context. Because a provider has been explicitly loaded, the default provider will not automatically load. Which means code using the default context by accident will fail because no algorithms will be available. love that for us, honestly πŸ’€

See "Library Context" in ossl-guide-migration(7) for additional information about the Library Context.

πŸŽ€ Using Encoders and Decoders with the FIPS module πŸŽ€

Encoders and decoders are used to read and write keys or parameters from or to some external format (for example a PEM file) ✨. If your application generates keys or parameters that then need to be written into PEM or DER format, then it's likely you'll need to use an encoder to do this. Similarly, you need a decoder to read previously saved keys and parameters. In most cases this will be invisible to you if you're using APIs that existed in OpenSSL 1.1.1 or earlier, such as i2d_PrivateKey(3). However, the appropriate encoder/decoder will need to be available in the library context associated with the key or parameter object. The built-in OpenSSL encoders and decoders are implemented in both the default and base providers, and are not in the FIPS module boundary. But since they're not cryptographic algorithms themselves, it's still possible to use them in conjunction with the FIPS module β€” which is exactly why these encoders/decoders have the "fips=yes" property against them πŸ¦‹. You should ensure that either the default or base provider is loaded into the library context in this case.

πŸ’œ Using the FIPS module in SSL/TLS πŸ’œ

Writing an application that uses libssl in conjunction with the FIPS module is much the same as writing a normal libssl application, bestie, no stress 🌸. If you're using global properties and the default library context to specify usage of FIPS validated algorithms, then this will happen automatically for all cryptographic algorithms in libssl. If you're using a nondefault library context to load the FIPS provider, then you can supply this to libssl using the function SSL_CTX_new_ex(3). It works as a drop in replacement for the function SSL_CTX_new(3), except it gives you the capability to specify the library context to be used πŸ‘‘. You can also use the same function to specify libssl specific properties to use. double duty, we love to see it.

In this first example we create two SSL_CTX objects using two different library contexts:

/*
 * We assume that a nondefault library context with the FIPS
 * provider loaded has been created called fips_libctx.
 */
SSL_CTX *fips_ssl_ctx = SSL_CTX_new_ex(fips_libctx, "fips=yes", TLS_method());
/*
 * We assume that a nondefault library context with the default
 * provider loaded has been created called non_fips_libctx.
 */
SSL_CTX *non_fips_ssl_ctx = SSL_CTX_new_ex(non_fips_libctx, NULL,
                                           TLS_method());

In this second example we create two SSL_CTX objects using different properties to specify FIPS usage:

/*
 * The "fips=yes" property includes all FIPS approved algorithms
 * as well as encoders from the default provider that are allowed
 * to be used. The NULL below indicates that we are using the
 * default library context.
 */
SSL_CTX *fips_ssl_ctx = SSL_CTX_new_ex(NULL, "fips=yes", TLS_method());
/*
 * The "provider!=fips" property allows algorithms from any
 * provider except the FIPS provider
 */
SSL_CTX *non_fips_ssl_ctx = SSL_CTX_new_ex(NULL, "provider!=fips",
                                           TLS_method());

⭐ Confirming that an algorithm is being provided by the FIPS module ⭐

So you wanna prove it? A chain of links needs to be followed to go from an algorithm instance to the provider that implements it πŸ”₯. The process is similar for all algorithms, but here the example of a digest is used.

To go from an EVP_MD_CTX to an EVP_MD, use EVP_MD_CTX_md(3). To go from the EVP_MD to its OSSL_PROVIDER, use EVP_MD_get0_provider(3). To extract the name from the OSSL_PROVIDER, use OSSL_PROVIDER_get0_name(3). chain complete, case closed πŸ’…

πŸ“ FIPS indicators πŸ“

FIPS indicators have been added to the FIPS provider in OpenSSL 3.4, bestie, this is new new ✨. FIPS 140-3 requires indicators to be used if the FIPS provider allows non approved algorithms. An algorithm is approved if it passes all required checks, such as minimum key size. By default an error will occur if any check fails 😀.

For backwards compatibility, individual algorithms may override the checks by using either an option in the FIPS configuration (See "FIPS indicator options" in fips_config(5)) OR in code using an algorithm context setter. Heads up though: overriding the check means that the algorithm is not FIPS compliant. you've been warned πŸ’€

OSSL_INDICATOR_set_callback(3) can be called to register a callback to log unapproved algorithms πŸ“. At the end of any algorithm operation, the approved status can be queried using an algorithm context getter to retrieve the indicator (e.g. "fips-indicator"). An example of an algorithm context setter is "key-check" in "Supported parameters" in EVP_KDF-HKDF(7).

Every algorithm implementation advertised with the property "fips=yes" by the OpenSSL FIPS provider exposes "fips-indicator" as a gettable context parameter 🌈. The getter returns 1 when the operation is approved. And listen β€” an absent indicator must not be interpreted as approval. no indicator is not a yes, periodt πŸ™ˆ

Algorithms that get advertised with fips=no? Yeah bestie, those are unapproved, no cap. And whenever one of those implementations exposes fips-indicator, the getter hands you back 0. Quick reality check though: providers that aren't the OpenSSL FIPS provider literally do not have to expose this parameter at all. So an absent indicator, or an indicator that says 0, does not mean "yay FIPS approved." It just means no. Periodt.

Some algorithms lowkey switch teams β€” approved to unapproved β€” depending on their parameters or on what operation you're doing. The following ones run extra checks:

  • DSA Key generation β€” not approved anymore, bestie. Go read "DSA parameters" in EVP_PKEY-DSA(7).
  • DSA Signatures β€” DSA signature generation is also no longer approved. Peep "Signature Parameters" in EVP_SIGNATURE-DSA(7).
  • ECDSA Signatures β€” see "ECDSA Signature Parameters" in EVP_SIGNATURE-ECDSA(7).
  • EC Key Generation β€” see "Common EC parameters" in EVP_PKEY-EC(7).
  • ML-KEM Key Encapsulation β€” if you supply that test-only ikme parameter, the operation becomes unapproved. See "ML-KEM KEM parameters" in EVP_KEM-ML-KEM(7).
  • ML-DSA and SLH-DSA Signatures β€” supplying test entropy makes the operation unapproved, and ML-DSA operations that disable the standard message encoding are unapproved too. See "FIPS indicator" in EVP_SIGNATURE-ML-DSA(7) and "FIPS indicator" in EVP_SIGNATURE-SLH-DSA(7).
  • RSA Key Generation β€” supplying the parameters reserved for FIPS module testing makes it unapproved. See "RSA key generation FIPS indicator" in EVP_PKEY-RSA(7).
  • RSA Encryption β€” "pkcs1" padding is no longer approved, and that's the whole vibe. See "RSA Asymmetric Cipher parameters" in EVP_ASYM_CIPHER-RSA(7) and "RSA KEM parameters" in EVP_KEM-RSA(7).
  • RSA Signatures β€” see "Signature Parameters" in EVP_SIGNATURE-RSA(7).
  • DRBGS β€” see "Supported parameters" in EVP_RAND-HASH-DRBG(7) and EVP_RAND-HMAC-DRBG(7)/Supported parameters>.
  • DES β€” Triple-DES is no longer approved for encryption (yes, the original really says "not longer", I'm keeping it, it's iconic). See "Parameters" in EVP_CIPHER-DES(7).
  • DH β€” see "DH and DHX key exchange parameters" in EVP_KEYEXCH-DH(7).
  • ECDH β€” see "ECDH Key Exchange parameters" in EVP_KEYEXCH-ECDH(7).
  • KDFS β€” see the relevant KDF docs, e.g. "Supported parameters" in EVP_KDF-HKDF(7).
  • CMAC and KMAC β€” see "Supported parameters" in EVP_MAC-CMAC(7) and "Supported parameters" in EVP_MAC-KMAC(7).

Now here's the tea: the following FIPS algorithms are unapproved and they do use the fips-indicator.

  • RAND-TEST-RAND β€” see "Supported parameters" in EVP_RAND-TEST-RAND(7). The indicator callback is NOT triggered for this algorithm, because it's used internally for non-security purposes. Okay so it's giving behind-the-scenes intern energy.
  • X25519 and X448 Key Generation and Key Exchange
  • X448MLKEM1024 Key Generation and Key Encapsulation

Meanwhile, the other hybrid ML-KEM algorithms that the FIPS provider ships are approved, including X25519MLKEM768, SecP256r1MLKEM768 and SecP384r1MLKEM1024. Slay.

The unapproved (non FIPS validated) algorithms have a property query value of "fips=no". Simple as that.

And the following algorithms use a unique indicator and do not trigger the indicator callback:

  • AES-GCM ciphers support the indicator "iv-generated" β€” see "PARAMETERS" in EVP_EncryptInit(3) for further info. The AES-GCM "fips-indicator" also accounts for the IV source and returns 0 after encryption with an externally supplied IV. It also returns 0 for GCM tag lengths other than 4, 8, or 12 through 16 bytes. πŸ™ˆ
  • ECDSA and RSA Signatures support the indicator "verify-message" β€” see "ECDSA Signature Parameters" in EVP_SIGNATURE-ECDSA(7) and "Signature Parameters" in EVP_SIGNATURE-RSA(7) for further information. The signature "fips-indicator" also accounts for this value and returns 0 when verification is performed directly on a caller-supplied digest.

πŸ“Œ NOTES πŸ“Œ

Some released versions of OpenSSL literally do not include a validated FIPS provider. To figure out which versions went through the validation process, please refer to the OpenSSL Downloads page <https://openssl-library.org/source/>. And if you need FIPS-approved functionality, it is essential that you build your FIPS provider using one of the validated versions listed there. No shortcuts, bestie. πŸ’…

Normally though, you can use a FIPS provider built from one of the validated versions alongside libcrypto and libssl compiled from any supported release from OpenSSL 3.0 onwards. Provider compatibility is maintained backward and forward across these releases β€” including future major release series β€” for as long as the module remains supported. This flexibility is honestly the moment, because it lets you fix bugs and patch CVEs that fall outside the FIPS boundary. πŸ”₯

And since the FIPS provider still supports non-FIPS validated algorithms, the property query "fips=yes" is mandatory for applications that want to operate in a FIPS approved manner. Like, mandatory mandatory. Don't skip it.


πŸ‘€ SEE ALSO πŸ‘€

ossl-guide-migration(7), crypto(7), fips_config(5), <https://www.openssl.org/source/>


πŸ•°οΈ HISTORY πŸ•°οΈ

The FIPS module guide was created for use with the new FIPS provider in OpenSSL 3.0. FIPS indicators were added in OpenSSL 3.4. So if you're on something older, that's why the vibes are off. 😭


Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


And that's the whole page, bestie β€” this one came from OpenSSL 4.0.3, dated 2026-09-29, and it's the FIPS_MODULE(7SSL) manual. Now go forth and query fips=yes like the approved main character you are. πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from