{"id":433,"date":"2026-04-13T02:43:57","date_gmt":"2026-04-13T02:43:57","guid":{"rendered":"https:\/\/poznayu.com\/en\/?p=433"},"modified":"2026-04-13T02:44:49","modified_gmt":"2026-04-13T02:44:49","slug":"how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers","status":"publish","type":"post","link":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/","title":{"rendered":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers"},"content":{"rendered":"<div style='text-align:right' class='yasr-auto-insert-visitor'><\/div><p data-start=\"84\" data-end=\"355\">Every time you visit a website, your browser leaves behind a digital fingerprint. Even if you clear your cookies, change your IP address, or browse in incognito mode, these hidden markers can still identify and block you. This process is known as &#8220;device fingerprinting.&#8221;<\/p>\n<p data-start=\"84\" data-end=\"355\"><!--more--><\/p>\n<p data-start=\"357\" data-end=\"560\">In this article, I&#8217;ll explore how you can collect unique device characteristics using PHP, JavaScript, and HTML5 to reliably differentiate one visitor from another and, if necessary, block troublemakers.<\/p>\n<p data-section-id=\"1e4emyl\" data-start=\"562\" data-end=\"620\"><strong>What is Device Fingerprinting and Why is it Important?<\/strong><\/p>\n<p data-start=\"622\" data-end=\"799\">Imagine you\u2019re at the entrance of a store, and instead of photographing someone\u2019s face, you take a picture of their shoes, walking style, favorite hand, and how they open doors.<\/p>\n<p data-start=\"801\" data-end=\"1116\">You can create a complete profile without ever seeing their face. Device fingerprinting works similarly: it collects not a username or password, but the &#8220;signature&#8221; of the browser and device. This is useful for blocking persistent spammers who create hundreds of fake accounts or stopping bots trying to steal data.<\/p>\n<p data-start=\"1118\" data-end=\"1466\">Unlike cookies, which can be easily deleted, device fingerprints stay with the device for a much longer time. The Electronic Frontier Foundation (EFF) discovered that over 80% of browsers have a unique combination of such parameters. This means that while it\u2019s technically possible to spoof the system with small tricks, it\u2019s difficult to pull off.<\/p>\n<h2 data-section-id=\"s9gk9k\" data-start=\"1643\" data-end=\"1689\">The Key Criteria for Device Fingerprinting<\/h2>\n<p data-start=\"1691\" data-end=\"1843\">Modern information-gathering methods can be broken down into several layers. The more parameters you collect, the more accurate the fingerprint will be.<\/p>\n<ol data-start=\"1845\" data-end=\"3413\">\n<li data-section-id=\"14xcne1\" data-start=\"1845\" data-end=\"2242\"><strong data-start=\"1848\" data-end=\"1883\">Basic Browser and OS Properties<\/strong><br data-start=\"1883\" data-end=\"1886\" \/>This is the simplest and most obvious layer. It includes the browser version, interface language, time zone, operating system type, installed fonts, and plugins. You can easily collect this data via JavaScript and the <code data-start=\"2107\" data-end=\"2118\">navigator<\/code> object. While it\u2019s possible to spoof these, combining them with more advanced parameters provides a high level of accuracy.<\/li>\n<li data-section-id=\"cybdrp\" data-start=\"2244\" data-end=\"2671\"><strong data-start=\"2247\" data-end=\"2281\">Screen and Graphics Parameters<\/strong><br data-start=\"2281\" data-end=\"2284\" \/>Every monitor has its own resolution, color depth, and pixel ratio. But the most powerful method here is <strong data-start=\"2392\" data-end=\"2417\">Canvas Fingerprinting<\/strong>. The browser draws an invisible image on an HTML5 canvas in the background and then turns it into a hash. Due to differences in graphics cards, drivers, and smoothing algorithms, the resulting hash will be unique for each &#8220;device + browser&#8221; combination.<\/li>\n<li data-section-id=\"1faijnw\" data-start=\"2673\" data-end=\"3047\"><strong data-start=\"2676\" data-end=\"2707\">Hardware and Audio Features<\/strong><br data-start=\"2707\" data-end=\"2710\" \/>Through WebGL, you can determine your device&#8217;s graphics card model and driver. Additionally, the Web Audio API reveals how the device processes sound. Even if you never listen to music on the site, a script can generate an inaudible signal and analyze distortions caused by your sound card. This is one of the most stable identifiers.<\/li>\n<li data-section-id=\"xh7ddu\" data-start=\"3049\" data-end=\"3413\"><strong data-start=\"3052\" data-end=\"3103\">Behavioral Metrics (Clicks and Mouse Movements)<\/strong><br data-start=\"3103\" data-end=\"3106\" \/>This is the most advanced technique. It tracks how a person moves their cursor: smoothly or in jerks, the speed at which buttons are clicked, and the path taken. Bots typically move in a straight line at a constant speed, while humans have accelerations, decelerations, and slight &#8220;shakes&#8221; in their hand.<\/li>\n<\/ol>\n<h2 data-section-id=\"1pmgjyg\" data-start=\"3415\" data-end=\"3455\">How to Implement This: Code Examples<\/h2>\n<blockquote>\n<p data-start=\"3457\" data-end=\"3956\"><strong data-start=\"3457\" data-end=\"3472\">Disclaimer:<\/strong> The following code examples, architectural diagrams, and discussions about device fingerprinting methods are not ready-made commercial solutions. They are meant to illustrate ideas and demonstrate principles. To implement them in a real project, you must account for the specific environment (browsers, CSP restrictions, security policies) and infrastructure specifics. The provided snippets will need adaptation, refactoring, and testing for your unique business logic and workload.<\/p>\n<\/blockquote>\n<p data-start=\"3958\" data-end=\"4073\">It all starts on the frontend. Using JavaScript, we gather all the parameters and send them to the server via AJAX.<\/p>\n<ol data-start=\"4075\" data-end=\"4239\">\n<li data-section-id=\"orteor\" data-start=\"4075\" data-end=\"4239\"><strong data-start=\"4078\" data-end=\"4111\">Canvas Fingerprint Collection<\/strong><br data-start=\"4111\" data-end=\"4114\" \/>One of the most reliable methods. The script creates a hidden image with text and a background and then computes its hash.<\/li>\n<\/ol>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-js\" data-lang=\"JavaScript\"><code>async function getCanvasFingerprint() {\r\nconst canvas = document.createElement('canvas');\r\nconst ctx = canvas.getContext('2d');\r\ncanvas.width = 200;\r\ncanvas.height = 50;\r\nctx.textBaseline = 'top';\r\nctx.font = '14px Arial';\r\nctx.fillStyle = '#f60';\r\nctx.fillRect(0, 0, 100, 30);\r\nctx.fillStyle = '#069';\r\nctx.fillText('Browser Fingerprint', 5, 15);\r\nconst dataURL = canvas.toDataURL();\r\nconst hashBuffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(dataURL));\r\nconst hashArray = Array.from(new Uint8Array(hashBuffer));\r\nreturn hashArray.map(b =&gt; b.toString(16).padStart(2, '0')).join('');\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<ol start=\"2\" data-start=\"4884\" data-end=\"5005\">\n<li data-section-id=\"18elmtb\" data-start=\"4884\" data-end=\"5005\"><strong data-start=\"4887\" data-end=\"4928\">WebGL Data Collection (Graphics Card)<\/strong><br data-start=\"4928\" data-end=\"4931\" \/>This reveals not only the GPU model but also rendering characteristics.<\/li>\n<\/ol>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-js\" data-lang=\"JavaScript\"><code>function getWebGLFingerprint() {\r\nconst canvas = document.createElement('canvas');\r\nconst gl = canvas.getContext('webgl');\r\nif (!gl) return 'no_webgl';\r\nconst debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\r\nif (debugInfo) {\r\nconst vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);\r\nconst renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);\r\nreturn `${vendor}~${renderer}`;\r\n}\r\nreturn 'no_debug_info';\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<ol start=\"3\" data-start=\"5476\" data-end=\"5593\">\n<li data-section-id=\"1jjgd1u\" data-start=\"5476\" data-end=\"5593\"><strong data-start=\"5479\" data-end=\"5524\">Audio Fingerprint Collection (Sound Card)<\/strong><br data-start=\"5524\" data-end=\"5527\" \/>An inaudible signal is generated, and distortions are analyzed.<\/li>\n<\/ol>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-js\" data-lang=\"JavaScript\"><code>async function getAudioFingerprint() {\r\nconst audioCtx = new (window.AudioContext || window.webkitAudioContext)();\r\nconst oscillator = audioCtx.createOscillator();\r\nconst analyser = audioCtx.createAnalyser();\r\noscillator.connect(analyser);\r\noscillator.type = 'sine';\r\noscillator.frequency.value = 1000;\r\nconst dataArray = new Uint8Array(analyser.frequencyBinCount);\r\noscillator.start();\r\nanalyser.getByteFrequencyData(dataArray);\r\noscillator.stop();\r\nawait audioCtx.close();\r\nconst hashBuffer = await crypto.subtle.digest('SHA-256', dataArray);\r\nconst hashArray = Array.from(new Uint8Array(hashBuffer));\r\nreturn hashArray.map(b =&gt; b.toString(16).padStart(2, '0')).join('');\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<ol start=\"4\" data-start=\"6304\" data-end=\"6396\">\n<li data-section-id=\"lpzahk\" data-start=\"6304\" data-end=\"6396\"><strong data-start=\"6307\" data-end=\"6341\">Collection of Other Properties<\/strong><br data-start=\"6341\" data-end=\"6344\" \/>All this information is combined into one object.<\/li>\n<\/ol>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-js\" data-lang=\"JavaScript\"><code>function getBasicFingerprint() {\r\nreturn {\r\nuserAgent: navigator.userAgent,\r\nlanguage: navigator.language,\r\nplatform: navigator.platform,\r\nhardwareConcurrency: navigator.hardwareConcurrency,\r\ndeviceMemory: navigator.deviceMemory,\r\nscreenResolution: `${screen.width}x${screen.height}`,\r\ncolorDepth: screen.colorDepth,\r\ntimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\r\nplugins: Array.from(navigator.plugins).map(p =&gt; p.name).join(','),\r\ncanvas: await getCanvasFingerprint(),\r\nwebgl: getWebGLFingerprint(),\r\naudio: await getAudioFingerprint()\r\n};\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<ol start=\"5\" data-start=\"7013\" data-end=\"7213\">\n<li data-section-id=\"y3boy3\" data-start=\"7013\" data-end=\"7213\"><strong data-start=\"7016\" data-end=\"7071\">Sending Data to the Server and Processing It in PHP<\/strong><br data-start=\"7071\" data-end=\"7074\" \/>After collecting the object, it\u2019s sent via POST in JSON format. On the backend, data should be sanitized to prevent spoofing and stored.<\/li>\n<\/ol>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>$data = json_decode(file_get_contents('php:\/\/input'), true);\r\n$userAgent = preg_replace('\/\\s+\/', ' ', trim($data['userAgent'] ?? ''));\r\n$screen = preg_replace('\/[^0-9x]\/', '', $data['screenResolution'] ?? '');\r\n$canvasHash = preg_replace('\/[^a-f0-9]\/', '', $data['canvas'] ?? '');\r\n$webgl = preg_replace('\/[^a-zA-Z0-9~_\\-]\/', '', $data['webgl'] ?? '');\r\n$audio = preg_replace('\/[^a-f0-9]\/', '', $data['audio'] ?? '');\r\n$fingerprintRaw = $userAgent . '|' . $screen . '|' . $data['colorDepth'] . '|' . $data['timezone'] . '|' . $canvasHash . '|' . $webgl . '|' . $audio;\r\n$fingerprintHash = hash_hmac('sha256', $fingerprintRaw, 'YOUR_SECRET_KEY');<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div>\n<h3 data-section-id=\"1cnsbya\" data-start=\"284\" data-end=\"337\">Determining the Device&#8217;s MAC Address via PHP \/ JS<\/h3>\n<p data-start=\"339\" data-end=\"472\">It&#8217;s <strong>not possible<\/strong> to directly obtain the MAC address of a user through PHP or JavaScript \u2014 this is a significant security limitation.<\/p>\n<p data-start=\"474\" data-end=\"761\">JavaScript in the browser does not have access to the hardware address of the network card by default. The only exception to this would be if the user has specifically installed and granted access to a special extension or desktop application, but that&#8217;s a different scenario altogether.<\/p>\n<p data-start=\"763\" data-end=\"1059\">There is also a &#8220;loophole&#8221; method via WebRTC, which can calculate the local IP, but not the MAC address, and modern browsers have long since blocked this method. On the server side, in PHP, you can obtain the MAC address of a client if the web server and the client are in the same local network.<\/p>\n<p data-start=\"1061\" data-end=\"1182\">The simplest way is to execute the system command <code data-start=\"1111\" data-end=\"1119\">arp -a<\/code>, which outputs a table matching IP addresses to MAC addresses.<\/p>\n<p data-start=\"1184\" data-end=\"1250\">Example PHP code for obtaining the MAC address in a local network:<\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>&lt;?php\r\n$ip = $_SERVER['REMOTE_ADDR'];\r\n\/\/ Check if the IP is within a local network (simplified)\r\nif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {\r\n\/\/ Execute the arp command\r\n$output = shell_exec(\"arp -a \" . escapeshellarg($ip));\r\n\/\/ Search for MAC address in the output\r\nif (preg_match('\/([a-f0-9]{2}[-:]){5}[a-f0-9]{2}\/i', $output, $matches)) {\r\necho \"MAC Address: \" . $matches[0];\r\n} else {\r\necho \"MAC Address not found\";\r\n}\r\n} else {\r\necho \"IP is not local\";\r\n}\r\n?&gt;<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><span style=\"font-size: 16px;\">This code works only if the web server and the client are in the same subnet (for example, within a corporate network). In real-world web conditions (over the Internet), this method does not work and is not meaningful. For global networks, MAC address identification is not reliable.<\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h2>Server-Side Logic, Storage, and Blocking<\/h2>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-start=\"7912\" data-end=\"7979\">Let&#8217;s first discuss the server-side logic for the user fingerprint.<\/p>\n<p data-start=\"7981\" data-end=\"8107\">Once the fingerprint hash is received on the server, it should be associated with the user account or placed in the blacklist.<\/p>\n<p data-start=\"8109\" data-end=\"8314\">The key principle here is: <strong data-start=\"8136\" data-end=\"8171\">a fingerprint is not a password<\/strong>, it\u2019s just a signal. It can\u2019t be used as the sole reason for blocking a user, or you risk blocking an entire office of users with the same IP.<\/p>\n<p data-start=\"8316\" data-end=\"8352\"><strong>Storing in the Database (MySQL)<\/strong><\/p>\n<p data-start=\"8354\" data-end=\"8423\">It\u2019s best to store trusted devices in a JSON field in the user table.<\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>ALTER TABLE users ADD COLUMN trusted_devices JSON DEFAULT NULL;<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<p><strong>Logic for Logging In<\/strong><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<ol data-start=\"8528\" data-end=\"8955\">\n<li data-section-id=\"utn3lv\" data-start=\"8528\" data-end=\"8558\">User enters their password.<\/li>\n<li data-section-id=\"10nioqi\" data-start=\"8559\" data-end=\"8654\">The server computes the current fingerprint hash (based on the data sent from the frontend).<\/li>\n<li data-section-id=\"1aj35fu\" data-start=\"8655\" data-end=\"8743\">The server checks this hash against the <code data-start=\"8698\" data-end=\"8715\">trusted_devices<\/code> column of the current user.<\/li>\n<li data-section-id=\"q2k7is\" data-start=\"8744\" data-end=\"8786\">If the hash is found, login is allowed.<\/li>\n<li data-section-id=\"1p3n3k5\" data-start=\"8787\" data-end=\"8955\">If the hash is not found, a verification code is sent to the user (via email or SMS). After successful code entry, this hash is added to the <code data-start=\"8931\" data-end=\"8948\">trusted_devices<\/code> array.<\/li>\n<\/ol>\n<p data-start=\"8957\" data-end=\"9066\">This approach is secure because an attacker cannot add their device without access to the user&#8217;s email\/phone.<\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>$trusted = json_decode($user['trusted_devices'] ?? '[]', true);\r\nif (!in_array($fingerprintHash, $trusted)) {\r\n\/\/ Send verification code\r\n$_SESSION['pending_fingerprint'] = $fingerprintHash;\r\nexit('verify_required');\r\n}\r\n\/\/ Verification passed, login successful<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div>\n<p data-section-id=\"1a8dpzc\" data-start=\"150\" data-end=\"177\"><strong>Block Logic Based on IP<\/strong><\/p>\n<p data-start=\"179\" data-end=\"360\">The IP address is the most accessible, but also the least reliable identifier. It can be used as a secondary signal in combination with other methods, but not as the sole criterion.<\/p>\n<p data-start=\"362\" data-end=\"401\"><strong>Example: Simple IP Blacklist Block<\/strong><\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>$blacklistedIps = ['192.168.1.100', '203.0.113.5'];\r\n$userIp = getUserIP(); \/\/ function from previous message\r\nif (in_array($userIp, $blacklistedIps)) {\r\ndie('Access denied. Your IP is on the blacklist.');\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><span style=\"font-size: 16px;\">This method is useful for targeting specific offenders. However, many users have dynamic IPs (which change when the router is rebooted) or use VPNs\/proxies, so the blacklist becomes outdated quickly. Additionally, one IP address can be shared by hundreds of people (such as in offices or mobile networks), and you could block innocent users.<\/span><\/div>\n<div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-start=\"967\" data-end=\"1040\"><strong>Example: Limiting the Number of Attempts from One IP (Rate Limiting)<\/strong><\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>$ip = getUserIP();\r\n$key = 'login_attempts_' . $ip;\r\n$attempts = apcu_fetch($key) ?: 0;\r\nif ($attempts &gt; 5) {\r\ndie('Too many login attempts from your IP. Please try again later.');\r\n}\r\napcu_store($key, ++$attempts, 300); \/\/ block for 5 minutes<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><span style=\"font-size: 16px;\">This is a defense against brute force (password guessing) attacks. It doesn\u2019t block the user forever, but it slows down the attack. However, if the attacker uses a network of multiple IPs (a botnet), this protection won\u2019t be effective. It\u2019s a good idea to complement this with CAPTCHA after several failed attempts.<\/span><\/div>\n<div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-start=\"1613\" data-end=\"1660\"><strong>Example: Comparing IPs to Detect Anomalies<\/strong><\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>session_start();\r\n$currentIp = getUserIP();\r\nif (isset($_SESSION['user_ip']) &amp;&amp; $_SESSION['user_ip'] !== $currentIp) {\r\n\/\/ IP has changed, possibly session hijacking\r\nerror_log(\"Warning: IP changed from {$_SESSION['user_ip']} to $currentIp for user {$_SESSION['user_id']}\");\r\n\/\/ Don\u2019t block immediately, but require re-authentication\r\nsession_destroy();\r\nheader('Location: \/login?reason=ip_changed');\r\nexit;\r\n}\r\n$_SESSION['user_ip'] = $currentIp;<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><span style=\"font-size: 16px;\">If the IP changes suddenly during an active session (for example, from Russian to Ukrainian), it could indicate that an attacker has stolen the session cookies and is trying to use them from a different device. However, mobile operators\u2019 IPs can change when switching cell towers, and users with VPNs may change countries when reconnecting. Therefore, this behavior is not always an attack but should raise attention (for example, by requesting an additional password).<\/span><\/div>\n<div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-section-id=\"ypjbad\" data-start=\"2611\" data-end=\"2639\">Important Considerations:<\/p>\n<ul data-start=\"2641\" data-end=\"3033\">\n<li data-section-id=\"63qjb5\" data-start=\"2641\" data-end=\"2818\"><strong data-start=\"2643\" data-end=\"2678\">IP is not a personal identifier<\/strong>: One IP can belong to an entire office, a public Wi-Fi network, or a mobile operator. Blocking by IP can affect thousands of regular users.<\/li>\n<li data-section-id=\"1esyjwn\" data-start=\"2819\" data-end=\"2903\"><strong data-start=\"2821\" data-end=\"2859\">Proxies and VPNs easily change IPs<\/strong>: Attackers often use them to bypass blocks.<\/li>\n<li data-section-id=\"10lqqki\" data-start=\"2904\" data-end=\"3033\"><strong data-start=\"2906\" data-end=\"2935\">IPv6 complicates analysis<\/strong>: Each device can have its own public IP, but the ranges are vast, and dynamic changes are common.<\/li>\n<\/ul>\n<p data-start=\"3035\" data-end=\"3268\">The IP address should only be used as one of many signals in a risk assessment system, not as the sole reason for blocking. Combine it with device fingerprints, behavioral metrics, and two-factor authentication for stronger security.<\/p>\n<h3>How to Fully Block a User<\/h3>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-start=\"9373\" data-end=\"9485\">If you want to block all devices from a particular user (not just one account), you can keep a global ban table.<\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"relative\">\n<div class=\"w-full overflow-x-hidden overflow-y-auto\">\n<div class=\"relative z-0 flex max-w-full\">\n<div id=\"code-block-viewer\" dir=\"ltr\" class=\"q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch \u037c5 \u037cj\">\n<div class=\"cm-scroller\">\n<div class=\"cm-content q9tKkq_readonly\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>CREATE TABLE banned_fingerprints (\r\nfingerprint_hash VARCHAR(64) PRIMARY KEY,\r\nreason TEXT,\r\nbanned_at DATETIME DEFAULT CURRENT_TIMESTAMP\r\n);<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><span style=\"font-size: 16px;\">On each request, the server computes the fingerprint hash and checks it against this table. If the hash is found, an access error is returned.<\/span><\/div>\n<div><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<h2 data-section-id=\"gfnio4\" data-start=\"168\" data-end=\"209\">Method Limitations and Practical Tips<\/h2>\n<p data-start=\"211\" data-end=\"307\">No device fingerprinting method gives you a 100% guarantee. Here&#8217;s what you&#8217;ll face in practice.<\/p>\n<ul>\n<li data-start=\"309\" data-end=\"333\"><strong>Changing Parameters.\u00a0<\/strong>If a user updates their browser, installs a new graphics card, or switches to &#8220;Incognito&#8221; mode, some parameters may change, causing a legitimate user to fail the fingerprint check. The solution is to avoid strict blocking and instead use fingerprinting as one of several risk signals, alongside others like the IP address or anomalous behavior.<\/li>\n<li data-start=\"680\" data-end=\"708\"><strong>Data Collection Caution. <\/strong>Many countries have laws that regulate tracking methods without explicit user consent. Always include a privacy policy on your site and inform users that you&#8217;re collecting technical information.<\/li>\n<li data-start=\"905\" data-end=\"949\"><strong>Modern Browsers Are Complicating Things.\u00a0<\/strong>As of 2026, browsers like Firefox and Safari have tightened their policies. They may return obfuscated Canvas data or block WebGL for unauthorized scripts. Your code should handle situations where the browser refuses to share parameters.<\/li>\n<\/ul>\n<p data-start=\"1189\" data-end=\"1470\">The best strategy is to combine device fingerprinting with other data points: the IP address, browsing history, request frequency, and CAPTCHA. This way, you&#8217;ll create a system that&#8217;s harder to bypass and will reduce the likelihood of blocking regular users due to false positives.<\/p>\n<h2 data-section-id=\"10x17n9\" data-start=\"1477\" data-end=\"1552\">What Happens When a User Switches from LAN to Wi-Fi in the Same Network<\/h2>\n<p data-start=\"1554\" data-end=\"1753\">When a user changes their connection method (for example, from Ethernet to Wi-Fi) within the same home or office, many of their fingerprinting parameters may remain the same, while others may change.<\/p>\n<p data-start=\"1755\" data-end=\"1824\"><strong>Example 1: Ignoring Network Parameters in the Stable Fingerprint<\/strong><\/p>\n<p data-start=\"1826\" data-end=\"1884\"><strong data-start=\"1826\" data-end=\"1843\">What happens.<\/strong><br data-start=\"1843\" data-end=\"1846\" \/>When switching from Ethernet to Wi-Fi:<\/p>\n<ul data-start=\"1886\" data-end=\"2173\">\n<li data-section-id=\"5c18f8\" data-start=\"1886\" data-end=\"1968\">The external IP address typically stays the same (unless the router changes it).<\/li>\n<li data-section-id=\"x767hu\" data-start=\"1969\" data-end=\"2173\">However, internal parameters accessible via JavaScript may change: <code data-start=\"2038\" data-end=\"2065\">navigator.connection.type<\/code> may switch from &#8216;ethernet&#8217; to &#8216;wifi&#8217;, and <code data-start=\"2108\" data-end=\"2113\">rtt<\/code> (round-trip time) and <code data-start=\"2136\" data-end=\"2146\">downlink<\/code> (speed) could also change.<\/li>\n<\/ul>\n<p data-start=\"2175\" data-end=\"2422\"><strong data-start=\"2175\" data-end=\"2184\">Risk: <\/strong>If you include these parameters in the fingerprint hash, the user will receive a new fingerprint after switching, and they may be required to go through verification again (e.g., entering a code from email). This could frustrate users.<\/p>\n<p data-start=\"2424\" data-end=\"2863\"><strong data-start=\"2424\" data-end=\"2437\">Solution: <\/strong>Exclude any parameters that may change during the same session or by the same user without a device change. Stable parameters include: Canvas fingerprint, WebGL data, Audio fingerprint, font list, browser version, time zone, and screen resolution (if unchanged). Network parameters (connection type, RTT, IP) should either not be used or should be considered as separate risk signals, not as part of the primary identifier.<\/p>\n<p data-start=\"2870\" data-end=\"2935\"><strong>Example 2: Merging Fingerprints via a &#8220;Trusted Devices Pool&#8221;<\/strong><\/p>\n<p data-start=\"2937\" data-end=\"3202\"><strong data-start=\"2937\" data-end=\"2954\">What happens.<\/strong><br data-start=\"2954\" data-end=\"2957\" \/>In some scenarios (for example, public Wi-Fi in a caf\u00e9, where the IP changes often), you can&#8217;t rely even on partial stability. But if the switch happens within the same private network (like at home or in an office), you can apply smarter logic.<\/p>\n<p data-start=\"3204\" data-end=\"3500\"><strong data-start=\"3204\" data-end=\"3217\">Solution: <\/strong>With each login attempt, the server calculates the fingerprint hash. If the hash doesn&#8217;t match any previously saved hashes for the user, but part of the parameters (like Canvas, WebGL, and Audio) match an already known device and only the network data has changed, the server can:<\/p>\n<ul data-start=\"3502\" data-end=\"3744\">\n<li data-section-id=\"1durhyp\" data-start=\"3502\" data-end=\"3601\">Skip requiring re-verification and simply add the new hash to the user&#8217;s list of trusted devices.<\/li>\n<li data-section-id=\"1e5uexp\" data-start=\"3602\" data-end=\"3744\">Or, if only <code data-start=\"3616\" data-end=\"3643\">navigator.connection.type<\/code> and <code data-start=\"3648\" data-end=\"3653\">rtt<\/code> have changed, treat this as the same device and automatically update the fingerprint hash.<\/li>\n<\/ul>\n<p data-start=\"3746\" data-end=\"4091\"><strong data-start=\"3746\" data-end=\"3794\">Technically, you can implement this like so: <\/strong>Store not just one hash in the database, but multiple fields: a <code data-start=\"3861\" data-end=\"3874\">stable_hash<\/code> (without network parameters) and a <code data-start=\"3910\" data-end=\"3921\">full_hash<\/code> (with all parameters). When the network changes, the <code data-start=\"3975\" data-end=\"3986\">full_hash<\/code> will change, but if the <code data-start=\"4011\" data-end=\"4024\">stable_hash<\/code> remains, you can update the <code data-start=\"4053\" data-end=\"4064\">full_hash<\/code> without blocking the user.<\/p>\n<p data-start=\"4093\" data-end=\"4123\"><strong data-start=\"4093\" data-end=\"4123\">Server-side example (PHP):<\/strong><\/p>\n<div class=\"relative w-full mt-4 mb-1\">\n<div class=\"\">\n<div class=\"relative\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"h-full min-h-0 min-w-0\">\n<div class=\"border border-token-border-light border-radius-3xl corner-superellipse\/1.1 rounded-3xl\">\n<div class=\"h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse\/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback\">\n<div class=\"pointer-events-none absolute inset-x-4 top-12 bottom-4\">\n<div class=\"pointer-events-none sticky z-40 shrink-0 z-1!\">\n<div class=\"sticky bg-token-border-light\">\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>$stableFingerprint = hash('sha256', $canvas . $webgl . $audio . $userAgent);\r\n$fullFingerprint = hash('sha256', $stableFingerprint . $networkType . $rtt);\r\n\/\/ Check if the stable fingerprint is in the trusted list\r\nif (in_array($stableFingerprint, $userTrustedStable)) {\r\n\/\/ Same device, just update the full fingerprint\r\n$user-&gt;updateFullFingerprint($fullFingerprint);\r\n\/\/ Allow access\r\n} else {\r\n\/\/ Full verification needed\r\n}<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"\">\n<div class=\"\"><\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p data-start=\"4579\" data-end=\"4772\">No method is 100% foolproof, but combining stable hardware metrics with smart logic for updating fingerprints helps minimize false positives when users switch networks within the same location.<\/p>\n<div style='text-align:right' class='yasr-auto-insert-visitor'><\/div>","protected":false},"excerpt":{"rendered":"<p>Every time you visit a website, your browser leaves behind a digital fingerprint. Even if you clear your cookies, change [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":434,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"yasr_overall_rating":0,"yasr_post_is_review":"","yasr_auto_insert_disabled":"","yasr_review_type":"","footnotes":""},"categories":[137],"tags":[375,371,370,373,374,372],"class_list":["post-433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web","tag-block","tag-blocking","tag-computer-imprint","tag-ip","tag-javascript","tag-php"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How Device Fingerprinting Works: Protecting Websites from Bots and Spammers<\/title>\n<meta name=\"description\" content=\"Learn how to implement device fingerprinting using PHP, JavaScript, and HTML5 to accurately identify users and block intruders. Protect your site from bots and spammers.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers\" \/>\n<meta property=\"og:description\" content=\"Learn how to implement device fingerprinting using PHP, JavaScript, and HTML5 to accurately identify users and block intruders. Protect your site from bots and spammers.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/\" \/>\n<meta property=\"og:site_name\" content=\"Discover Something New Every Day!\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-13T02:43:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-13T02:44:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"770\" \/>\n\t<meta property=\"og:image:height\" content=\"440\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Ethan Carter\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ethan Carter\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers","description":"Learn how to implement device fingerprinting using PHP, JavaScript, and HTML5 to accurately identify users and block intruders. Protect your site from bots and spammers.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/","og_locale":"en_US","og_type":"article","og_title":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers","og_description":"Learn how to implement device fingerprinting using PHP, JavaScript, and HTML5 to accurately identify users and block intruders. Protect your site from bots and spammers.","og_url":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/","og_site_name":"Discover Something New Every Day!","article_published_time":"2026-04-13T02:43:57+00:00","article_modified_time":"2026-04-13T02:44:49+00:00","og_image":[{"width":770,"height":440,"url":"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg","type":"image\/jpeg"}],"author":"Ethan Carter","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Ethan Carter","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#article","isPartOf":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/"},"author":{"name":"Ethan Carter","@id":"https:\/\/poznayu.com\/en\/#\/schema\/person\/8b7cd0287993879c0753ec5f24b911e1"},"headline":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers","datePublished":"2026-04-13T02:43:57+00:00","dateModified":"2026-04-13T02:44:49+00:00","mainEntityOfPage":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/"},"wordCount":2113,"commentCount":0,"image":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#primaryimage"},"thumbnailUrl":"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg","keywords":["block","blocking","computer imprint","IP","JavaScript","php"],"articleSection":["Web"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/","url":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/","name":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers","isPartOf":{"@id":"https:\/\/poznayu.com\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#primaryimage"},"image":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#primaryimage"},"thumbnailUrl":"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg","datePublished":"2026-04-13T02:43:57+00:00","dateModified":"2026-04-13T02:44:49+00:00","author":{"@id":"https:\/\/poznayu.com\/en\/#\/schema\/person\/8b7cd0287993879c0753ec5f24b911e1"},"description":"Learn how to implement device fingerprinting using PHP, JavaScript, and HTML5 to accurately identify users and block intruders. Protect your site from bots and spammers.","breadcrumb":{"@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#primaryimage","url":"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg","contentUrl":"https:\/\/poznayu.com\/en\/wp-content\/uploads\/2026\/04\/user-identification-and-blocking.jpg","width":770,"height":440,"caption":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers"},{"@type":"BreadcrumbList","@id":"https:\/\/poznayu.com\/en\/how-device-fingerprinting-works-protecting-websites-from-bots-and-spammers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/poznayu.com\/en\/"},{"@type":"ListItem","position":2,"name":"How Device Fingerprinting Works: Protecting Websites from Bots and Spammers"}]},{"@type":"WebSite","@id":"https:\/\/poznayu.com\/en\/#website","url":"https:\/\/poznayu.com\/en\/","name":"Discover Something New Every Day!","description":"Your informational hub for useful tips, fascinating facts, in-depth reviews, top lists, and mysterious stories. Explore more!","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/poznayu.com\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/poznayu.com\/en\/#\/schema\/person\/8b7cd0287993879c0753ec5f24b911e1","name":"Ethan Carter","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/d487910763af2834ec95385e16ee1042fdecba0da3a68224eef0ccf2dced8e81?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/d487910763af2834ec95385e16ee1042fdecba0da3a68224eef0ccf2dced8e81?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d487910763af2834ec95385e16ee1042fdecba0da3a68224eef0ccf2dced8e81?s=96&d=mm&r=g","caption":"Ethan Carter"},"description":"I\u2019m Ethan Carter, an American developer and technical writer with more than 20 years of experience in systems and application programming. My core specialty is low-level development in Assembler: 22 years of hands-on work, including deep experience in code optimization, CPU architecture, and performance-critical solutions. I also hold a PhD in Assembler and have spent more than 18 years working with ASP.NET, building enterprise web systems, APIs, and scalable backend solutions. In addition, I have 9 years of experience in C++ and C#, along with 7 years of hands-on microcontroller programming in Assembler. Thanks to this mix of academic background and practical engineering experience, I can write about software architecture, low-level optimization, and modern development in a way that makes complex technical topics clear for a professional audience.","sameAs":["https:\/\/poznayu.com\/en\/category\/web\/"],"url":"https:\/\/poznayu.com\/en\/author\/coder\/"},false]}},"yasr_visitor_votes":{"stars_attributes":{"read_only":false,"span_bottom":false},"number_of_votes":1,"sum_votes":5},"_links":{"self":[{"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/posts\/433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/comments?post=433"}],"version-history":[{"count":2,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/posts\/433\/revisions"}],"predecessor-version":[{"id":436,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/posts\/433\/revisions\/436"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/media\/434"}],"wp:attachment":[{"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/media?parent=433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/categories?post=433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/poznayu.com\/en\/wp-json\/wp\/v2\/tags?post=433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}