diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 23a8919..0e3ceff 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.13)
 
 ### Basic compilation settings
 set (CMAKE_POSITION_INDEPENDENT_CODE TRUE)
+set (CMAKE_CXX_STANDARD 11)
 
 include_directories (
 	${CMAKE_CURRENT_SOURCE_DIR}
@@ -43,6 +44,18 @@ add_library (https-common STATIC
 	common/PlaintextConnection.cpp
 )
 
+add_library (https-windows-libraryloader STATIC EXCLUDE_FROM_ALL
+	windows/WindowsLibraryLoader.cpp
+)
+
+add_library (https-unix-libraryloader STATIC EXCLUDE_FROM_ALL
+	generic/UnixLibraryLoader.cpp
+)
+
+add_library (https-linktime-libraryloader STATIC EXCLUDE_FROM_ALL
+	generic/LinktimeLibraryLoader.cpp
+)
+
 add_library (https-curl STATIC EXCLUDE_FROM_ALL
 	generic/CurlClient.cpp
 )
@@ -68,6 +81,8 @@ add_library (https-wininet STATIC EXCLUDE_FROM_ALL
 )
 
 ### Flags
+set (LIBRARY_LOADER_DEFAULT "unix")
+
 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 	option (USE_CURL_BACKEND "Use the libcurl backend" ON)
 	option (USE_OPENSSL_BACKEND "Use the openssl backend" ON)
@@ -92,6 +107,8 @@ elseif (WIN32)
 
 	option (USE_WINSOCK "Use winsock instead of BSD sockets (windows-only)" ON)
 
+	set (LIBRARY_LOADER_DEFAULT "windows")
+
 	# Windows needs to link with Lua libraries
 	target_link_libraries(https ${LUA_LIBRARIES})
 elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
@@ -124,6 +141,8 @@ elseif (ANDROID)
 	target_link_libraries(https ${LUA_LIBRARIES})
 endif ()
 option (DEBUG_SCHANNEL "Enable debug output in schannel backend" OFF)
+set (LIBRARY_LOADER ${LIBRARY_LOADER_DEFAULT} CACHE STRING "Which method to use to dynamically load libraries")
+set_property (CACHE LIBRARY_LOADER PROPERTY STRINGS "unix;windows;linktime")
 
 set_target_properties(https PROPERTIES PREFIX "")
 
@@ -134,6 +153,7 @@ if (USE_CURL_BACKEND)
 	find_package (CURL REQUIRED)
 	include_directories (${CURL_INCLUDE_DIRS})
 	target_link_libraries (https https-curl)
+	target_link_libraries (https-linktime-libraryloader ${CURL_LIBRARY})
 endif ()
 
 if (USE_OPENSSL_BACKEND)
@@ -175,6 +195,19 @@ if (USE_WINSOCK)
 	set(HTTPS_USE_WINSOCK ON)
 endif ()
 
+if ("${LIBRARY_LOADER}" STREQUAL "unix")
+	set(HTTPS_LIBRARY_LOADER_UNIX ON)
+	target_link_libraries (https https-unix-libraryloader)
+elseif ("${LIBRARY_LOADER}" STREQUAL "windows")
+	set(HTTPS_LIBRARY_LOADER_WINDOWS ON)
+	target_link_libraries (https https-windows-libraryloader)
+elseif ("${LIBRARY_LOADER}" STREQUAL "linktime")
+	set(HTTPS_LIBRARY_LOADER_LINKTIME ON)
+	target_link_libraries (https https-linktime-libraryloader)
+else ()
+	message(WARNING "No library loader selected, backends that depend on dynamic loading will be broken")
+endif ()
+
 ### Generate config-generated.h
 add_compile_definitions(HTTPS_HAVE_CONFIG_GENERATED_H)
 configure_file (
diff --git a/src/android/AndroidClient.cpp b/src/android/AndroidClient.cpp
index a5994e2..2f11ecb 100644
--- a/src/android/AndroidClient.cpp
+++ b/src/android/AndroidClient.cpp
@@ -5,7 +5,7 @@
 #include <sstream>
 #include <type_traits>
 
-#include <dlfcn.h>
+#include "../common/LibraryLoader.h"
 
 // We want std::string that contains null byte, hence length of 1.
 // NOLINTNEXTLINE
@@ -52,10 +52,11 @@ static std::string getStringUTF(JNIEnv *env, jstring str)
 AndroidClient::AndroidClient()
 : HTTPSClient()
 {
+	LibraryLoader::handle *library = LibraryLoader::GetCurrentProcessHandle();
 	// Look for SDL_AndroidGetJNIEnv
-	SDL_AndroidGetJNIEnv = (decltype(SDL_AndroidGetJNIEnv)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetJNIEnv");
+	LibraryLoader::LoadSymbol(SDL_AndroidGetJNIEnv, library, "SDL_AndroidGetJNIEnv");
 	// Look for SDL_AndroidGetActivity
-	SDL_AndroidGetActivity = (decltype(SDL_AndroidGetActivity)) dlsym(RTLD_DEFAULT, "SDL_AndroidGetActivity");
+	LibraryLoader::LoadSymbol(SDL_AndroidGetActivity, library, "SDL_AndroidGetActivity");
 }
 
 bool AndroidClient::valid() const
diff --git a/src/common/HTTPS.cpp b/src/common/HTTPS.cpp
index 2667f2a..7569784 100644
--- a/src/common/HTTPS.cpp
+++ b/src/common/HTTPS.cpp
@@ -1,6 +1,7 @@
 #include "HTTPS.h"
 #include "config.h"
 #include "ConnectionClient.h"
+#include "LibraryLoader.h"
 
 #include <stdexcept>
 
@@ -65,6 +66,9 @@ static HTTPSClient *clients[] = {
 	nullptr,
 };
 
+// Call into the library loader to make sure it is linked in
+static LibraryLoader::handle* dummyProcessHandle = LibraryLoader::GetCurrentProcessHandle();
+
 HTTPSClient::Reply request(const HTTPSClient::Request &req)
 {
 	for (size_t i = 0; clients[i]; ++i)
diff --git a/src/common/LibraryLoader.h b/src/common/LibraryLoader.h
new file mode 100644
index 0000000..49ead46
--- /dev/null
+++ b/src/common/LibraryLoader.h
@@ -0,0 +1,20 @@
+#pragma once
+
+namespace LibraryLoader
+{
+	using handle = void;
+	using function = void();
+
+	handle *OpenLibrary(const char *name);
+	void CloseLibrary(handle *handle);
+	handle* GetCurrentProcessHandle();
+
+	function *GetFunction(handle *handle, const char *name);
+
+	template<class T>
+	inline bool LoadSymbol(T& var, handle *handle, const char *name)
+	{
+		var = reinterpret_cast<T>(GetFunction(handle, name));
+		return var != nullptr;
+	}
+}
diff --git a/src/common/config-generated.h.in b/src/common/config-generated.h.in
index 08403cb..625cd2a 100644
--- a/src/common/config-generated.h.in
+++ b/src/common/config-generated.h.in
@@ -6,3 +6,6 @@
 #cmakedefine HTTPS_BACKEND_WININET
 #cmakedefine HTTPS_USE_WINSOCK
 #cmakedefine DEBUG_SCHANNEL
+#cmakedefine HTTPS_LIBRARY_LOADER_WINDOWS
+#cmakedefine HTTPS_LIBRARY_LOADER_UNIX
+#cmakedefine HTTPS_LIBRARY_LOADER_LINKTIME
diff --git a/src/common/config.h b/src/common/config.h
index 4068e9b..97b8200 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -10,6 +10,7 @@
 #elif defined(WIN32) || defined(_WIN32)
 	#define HTTPS_BACKEND_SCHANNEL
 	#define HTTPS_USE_WINSOCK
+	#define HTTPS_LIBRARY_LOADER_WINDOWS
 	#include <winapifamily.h>
 	#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
 		// WinINet is only supported on desktop.
@@ -23,9 +24,13 @@
 	#endif
 #elif defined(__ANDROID__)
 	#define HTTPS_BACKEND_ANDROID
+	#define HTTPS_LIBRARY_LOADER_UNIX
 #elif defined(__APPLE__)
 	#define HTTPS_BACKEND_NSURL
+	#define HTTPS_LIBRARY_LOADER_UNIX
 #elif defined(linux) || defined(__linux) || defined(__linux__)
+	#define HTTPS_LIBRARY_LOADER_UNIX
+
 	#if defined __has_include
 		#if __has_include(<curl/curl.h>)
 			#define HTTPS_BACKEND_CURL
diff --git a/src/generic/CurlClient.cpp b/src/generic/CurlClient.cpp
index ac84689..8ae728c 100644
--- a/src/generic/CurlClient.cpp
+++ b/src/generic/CurlClient.cpp
@@ -1,8 +1,3 @@
-#ifdef _WIN32
-#define NOMINMAX
-#define WIN32_LEAN_AND_MEAN
-#endif
-
 #include "CurlClient.h"
 
 #ifdef HTTPS_BACKEND_CURL
@@ -12,30 +7,12 @@
 #include <sstream>
 #include <vector>
 
-// Dynamic library loader
-#ifdef _WIN32
-#include <windows.h>
-#else
-#include <dlfcn.h>
-#endif
-
 typedef struct StringReader
 {
 	const std::string *str;
 	size_t pos;
 } StringReader;
 
-template <class T>
-static inline bool loadSymbol(T &var, void *handle, const char *name)
-{
-#ifdef _WIN32
-	var = (T) GetProcAddress((HMODULE) handle, name);
-#else
-	var = (T) dlsym(handle, name);
-#endif
-	return var != nullptr;
-}
-
 CurlClient::Curl::Curl()
 : handle(nullptr)
 , loaded(false)
@@ -48,33 +25,35 @@ CurlClient::Curl::Curl()
 , slist_append(nullptr)
 , slist_free_all(nullptr)
 {
+	using namespace LibraryLoader;
+
 #ifdef _WIN32
-	handle = (void *) LoadLibraryA("libcurl.dll");
+	handle = OpenLibrary("libcurl.dll");
 #else
-	handle = dlopen("libcurl.so.4", RTLD_LAZY);
+	handle = OpenLibrary("libcurl.so.4");
 #endif
 	if (!handle)
 		return;
 
 	// Load symbols
 	decltype(&curl_global_init) global_init = nullptr;
-	if (!loadSymbol(global_init, handle, "curl_global_init"))
+	if (!LoadSymbol(global_init, handle, "curl_global_init"))
 		return;
-	if (!loadSymbol(global_cleanup, handle, "curl_global_cleanup"))
+	if (!LoadSymbol(global_cleanup, handle, "curl_global_cleanup"))
 		return;
-	if (!loadSymbol(easy_init, handle, "curl_easy_init"))
+	if (!LoadSymbol(easy_init, handle, "curl_easy_init"))
 		return;
-	if (!loadSymbol(easy_cleanup, handle, "curl_easy_cleanup"))
+	if (!LoadSymbol(easy_cleanup, handle, "curl_easy_cleanup"))
 		return;
-	if (!loadSymbol(easy_setopt, handle, "curl_easy_setopt"))
+	if (!LoadSymbol(easy_setopt, handle, "curl_easy_setopt"))
 		return;
-	if (!loadSymbol(easy_perform, handle, "curl_easy_perform"))
+	if (!LoadSymbol(easy_perform, handle, "curl_easy_perform"))
 		return;
-	if (!loadSymbol(easy_getinfo, handle, "curl_easy_getinfo"))
+	if (!LoadSymbol(easy_getinfo, handle, "curl_easy_getinfo"))
 		return;
-	if (!loadSymbol(slist_append, handle, "curl_slist_append"))
+	if (!LoadSymbol(slist_append, handle, "curl_slist_append"))
 		return;
-	if (!loadSymbol(slist_free_all, handle, "curl_slist_free_all"))
+	if (!LoadSymbol(slist_free_all, handle, "curl_slist_free_all"))
 		return;
 
 	global_init(CURL_GLOBAL_DEFAULT);
@@ -87,11 +66,7 @@ CurlClient::Curl::~Curl()
 		global_cleanup();
 
 	if (handle)
-#ifdef _WIN32
-		FreeLibrary((HMODULE) handle);
-#else
-		dlclose(handle);
-#endif
+		LibraryLoader::CloseLibrary(handle);
 }
 
 static char toUppercase(char c)
diff --git a/src/generic/CurlClient.h b/src/generic/CurlClient.h
index 30d4a9b..3761f36 100644
--- a/src/generic/CurlClient.h
+++ b/src/generic/CurlClient.h
@@ -7,6 +7,7 @@
 #include <curl/curl.h>
 
 #include "../common/HTTPSClient.h"
+#include "../common/LibraryLoader.h"
 
 class CurlClient : public HTTPSClient
 {
@@ -19,7 +20,7 @@ class CurlClient : public HTTPSClient
 	{
 		Curl();
 		~Curl();
-		void *handle;
+		LibraryLoader::handle *handle;
 		bool loaded;
 
 		decltype(&curl_global_cleanup) global_cleanup;
diff --git a/src/generic/LinktimeLibraryLoader.cpp b/src/generic/LinktimeLibraryLoader.cpp
new file mode 100644
index 0000000..ffca21d
--- /dev/null
+++ b/src/generic/LinktimeLibraryLoader.cpp
@@ -0,0 +1,66 @@
+#include "../common/config.h"
+#include "../common/LibraryLoader.h"
+
+#ifdef HTTPS_LIBRARY_LOADER_LINKTIME
+
+#include <cstring>
+
+#ifdef HTTPS_BACKEND_CURL
+#include <curl/curl.h>
+
+static char CurlHandle;
+#endif
+
+#if defined(HTTPS_BACKEND_OPENSSL) || defined(HTTPS_BACKEND_ANDROID)
+#	error "Selected backends that are not compatible with this loader"
+#endif
+
+namespace LibraryLoader
+{
+	handle *OpenLibrary(const char *name)
+	{
+#ifdef HTTPS_BACKEND_CURL
+		if (strstr(name, "libcurl") == name)
+			return reinterpret_cast<handle *>(&CurlHandle);
+#endif
+		return nullptr;
+	}
+
+	void CloseLibrary(handle *)
+	{
+	}
+
+	handle* GetCurrentProcessHandle()
+	{
+		return nullptr;
+	}
+
+	function *GetFunction(handle *handle, const char *name)
+	{
+#define RETURN_MATCHING_FUNCTION(func) \
+	if (strcmp(name, #func) == 0) \
+		return reinterpret_cast<function *>(&func);
+
+#ifdef HTTPS_BACKEND_CURL
+		if (handle == &CurlHandle)
+		{
+			RETURN_MATCHING_FUNCTION(curl_global_init);
+			RETURN_MATCHING_FUNCTION(curl_global_cleanup);
+			RETURN_MATCHING_FUNCTION(curl_easy_init);
+			RETURN_MATCHING_FUNCTION(curl_easy_cleanup);
+			RETURN_MATCHING_FUNCTION(curl_easy_setopt);
+			RETURN_MATCHING_FUNCTION(curl_easy_perform);
+			RETURN_MATCHING_FUNCTION(curl_easy_getinfo);
+			RETURN_MATCHING_FUNCTION(curl_slist_append);
+			RETURN_MATCHING_FUNCTION(curl_slist_free_all);
+		}
+#endif
+
+#undef RETURN_MATCHING_FUNCTION
+
+		return nullptr;
+	}
+}
+
+#endif // HTTPS_LIBRARY_LOADER_LINKTIME
+
diff --git a/src/generic/OpenSSLConnection.cpp b/src/generic/OpenSSLConnection.cpp
index e1f2dd7..658f24f 100644
--- a/src/generic/OpenSSLConnection.cpp
+++ b/src/generic/OpenSSLConnection.cpp
@@ -2,65 +2,60 @@
 
 #ifdef HTTPS_BACKEND_OPENSSL
 
-#include <dlfcn.h>
+#include "../common/LibraryLoader.h"
 
 // Not present in openssl 1.1 headers
 #define SSL_CTRL_OPTIONS 32
 
-template <class T>
-static inline bool loadSymbol(T &var, void *handle, const char *name)
-{
-	var = reinterpret_cast<T>(dlsym(handle, name));
-	return var != nullptr;
-}
-
 OpenSSLConnection::SSLFuncs::SSLFuncs()
 {
+	using namespace LibraryLoader;
+
 	valid = false;
 
 	// Try OpenSSL 1.1
-	void *sslhandle = dlopen("libssl.so.1.1", RTLD_LAZY);
-	void *cryptohandle = dlopen("libcrypto.so.1.1", RTLD_LAZY);
+	handle *sslhandle = OpenLibrary("libssl.so.1.1");
+	handle *cryptohandle = OpenLibrary("libcrypto.so.1.1");
 	// Try OpenSSL 1.0
 	if (!sslhandle || !cryptohandle)
 	{
-		sslhandle = dlopen("libssl.so.1.0.0", RTLD_LAZY);
-		cryptohandle = dlopen("libcrypto.so.1.0.0", RTLD_LAZY);
+		sslhandle = OpenLibrary("libssl.so.1.0.0");
+		cryptohandle = OpenLibrary("libcrypto.so.1.0.0");
 	}
 	// Try OpenSSL without version
 	if (!sslhandle || !cryptohandle)
 	{
-		sslhandle = dlopen("libssl.so", RTLD_LAZY);
-		cryptohandle = dlopen("libcrypto.so", RTLD_LAZY);
+		sslhandle = OpenLibrary("libssl.so");
+		cryptohandle = OpenLibrary("libcrypto.so");
 	}
 	// Give up
 	if (!sslhandle || !cryptohandle)
 		return;
 
 	valid = true;
-	valid = valid && (loadSymbol(library_init, sslhandle, "SSL_library_init") ||
-			loadSymbol(init_ssl, sslhandle, "OPENSSL_init_ssl"));
-
-	valid = valid && loadSymbol(CTX_new, sslhandle, "SSL_CTX_new");
-	valid = valid && loadSymbol(CTX_ctrl, sslhandle, "SSL_CTX_ctrl");
-	valid = valid && loadSymbol(CTX_set_verify, sslhandle, "SSL_CTX_set_verify");
-	valid = valid && loadSymbol(CTX_set_default_verify_paths, sslhandle, "SSL_CTX_set_default_verify_paths");
-	valid = valid && loadSymbol(CTX_free, sslhandle, "SSL_CTX_free");
-
-	valid = valid && loadSymbol(SSL_new, sslhandle, "SSL_new");
-	valid = valid && loadSymbol(SSL_free, sslhandle, "SSL_free");
-	valid = valid && loadSymbol(set_fd, sslhandle, "SSL_set_fd");
-	valid = valid && loadSymbol(connect, sslhandle, "SSL_connect");
-	valid = valid && loadSymbol(read, sslhandle, "SSL_read");
-	valid = valid && loadSymbol(write, sslhandle, "SSL_write");
-	valid = valid && loadSymbol(shutdown, sslhandle, "SSL_shutdown");
-	valid = valid && loadSymbol(get_verify_result, sslhandle, "SSL_get_verify_result");
-	valid = valid && loadSymbol(get_peer_certificate, sslhandle, "SSL_get_peer_certificate");
-
-	valid = valid && (loadSymbol(SSLv23_method, sslhandle, "SSLv23_method") ||
-			loadSymbol(SSLv23_method, sslhandle, "TLS_method"));
-
-	valid = valid && loadSymbol(check_host, cryptohandle, "X509_check_host");
+	valid = valid && (LoadSymbol(library_init, sslhandle, "SSL_library_init") ||
+			LoadSymbol(init_ssl, sslhandle, "OPENSSL_init_ssl"));
+
+	valid = valid && LoadSymbol(CTX_new, sslhandle, "SSL_CTX_new");
+	valid = valid && LoadSymbol(CTX_ctrl, sslhandle, "SSL_CTX_ctrl");
+	valid = valid && LoadSymbol(CTX_set_verify, sslhandle, "SSL_CTX_set_verify");
+	valid = valid && LoadSymbol(CTX_set_default_verify_paths, sslhandle, "SSL_CTX_set_default_verify_paths");
+	valid = valid && LoadSymbol(CTX_free, sslhandle, "SSL_CTX_free");
+
+	valid = valid && LoadSymbol(SSL_new, sslhandle, "SSL_new");
+	valid = valid && LoadSymbol(SSL_free, sslhandle, "SSL_free");
+	valid = valid && LoadSymbol(set_fd, sslhandle, "SSL_set_fd");
+	valid = valid && LoadSymbol(connect, sslhandle, "SSL_connect");
+	valid = valid && LoadSymbol(read, sslhandle, "SSL_read");
+	valid = valid && LoadSymbol(write, sslhandle, "SSL_write");
+	valid = valid && LoadSymbol(shutdown, sslhandle, "SSL_shutdown");
+	valid = valid && LoadSymbol(get_verify_result, sslhandle, "SSL_get_verify_result");
+	valid = valid && LoadSymbol(get_peer_certificate, sslhandle, "SSL_get_peer_certificate");
+
+	valid = valid && (LoadSymbol(SSLv23_method, sslhandle, "SSLv23_method") ||
+			LoadSymbol(SSLv23_method, sslhandle, "TLS_method"));
+
+	valid = valid && LoadSymbol(check_host, cryptohandle, "X509_check_host");
 
 	if (library_init)
 		library_init();
diff --git a/src/generic/UnixLibraryLoader.cpp b/src/generic/UnixLibraryLoader.cpp
new file mode 100644
index 0000000..55dc728
--- /dev/null
+++ b/src/generic/UnixLibraryLoader.cpp
@@ -0,0 +1,33 @@
+#include "../common/config.h"
+#include "../common/LibraryLoader.h"
+
+#ifdef HTTPS_LIBRARY_LOADER_UNIX
+
+#include <dlfcn.h>
+
+namespace LibraryLoader
+{
+	handle *OpenLibrary(const char *name)
+	{
+		return dlopen(name, RTLD_LAZY);
+	}
+
+	void CloseLibrary(handle *handle)
+	{
+		if (handle)
+			dlclose(handle);
+	}
+
+	handle* GetCurrentProcessHandle()
+	{
+		return RTLD_DEFAULT;
+	}
+
+	function *GetFunction(handle *handle, const char *name)
+	{
+		return reinterpret_cast<function *>(dlsym(handle, name));
+	}
+}
+
+#endif // HTTPS_LIBRARY_LOADER_UNIX
+
diff --git a/src/windows/WindowsLibraryLoader.cpp b/src/windows/WindowsLibraryLoader.cpp
new file mode 100644
index 0000000..d365aea
--- /dev/null
+++ b/src/windows/WindowsLibraryLoader.cpp
@@ -0,0 +1,35 @@
+#include "../common/config.h"
+#include "../common/LibraryLoader.h"
+
+#ifdef HTTPS_LIBRARY_LOADER_WINDOWS
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+
+#include <windows.h>
+
+namespace LibraryLoader
+{
+	handle *OpenLibrary(const char *name)
+	{
+		return reinterpret_cast<handle *>(LoadLibraryA(name));
+	}
+
+	void CloseLibrary(handle *handle)
+	{
+		if (handle)
+			FreeLibrary(reinterpret_cast<HMODULE>(handle));
+	}
+
+	handle* GetCurrentProcessHandle()
+	{
+		return reinterpret_cast<handle *>(GetModuleHandle(nullptr));
+	}
+
+	function *GetFunction(handle *handle, const char *name)
+	{
+		HMODULE nativeHandle = reinterpret_cast<HMODULE>(handle);
+		return reinterpret_cast<function *>(GetProcAddress(nativeHandle, name));
+	}
+}
+
+#endif // HTTPS_LIBRARY_LOADER_WINDOWS