In PHP, what do FILTER_VALIDATE_EMAIL and FILTER_VALIDATE_URL actually check on a sign-up form, and what do they still let through?
answer
- syntax only, never deliverability
- email needs a dotted domain
- Unicode local part needs a flag
- URL filter accepts any scheme
- allow-list http and https yourself
basics
~10 sBoth check syntax only. FILTER_VALIDATE_EMAIL confirms an address looks well-formed, not that it exists. FILTER_VALIDATE_URL requires a scheme but accepts any scheme, including file: and javascript: forms, so allow-list http and https yourself.
solid answer
~40 s`FILTER_VALIDATE_EMAIL` matches the address against a fixed pattern: at most 320 characters, a local part of permitted characters, and a domain with at least one dot, so `user@localhost` fails. Non-ASCII local parts need `FILTER_FLAG_EMAIL_UNICODE`, and the domain must be ASCII or punycode. It says nothing about whether the mailbox exists; only a confirmation email proves that. `FILTER_VALIDATE_URL` parses the string and requires a scheme; for `http` and `https` it also requires a valid host. Any other scheme passes with any host, and `file`, `mailto` and `news` pass without one, so `file:///etc/passwd` and `javascript://x/%0Aalert(1)` are valid URLs to it. Characters outside the URL character set, such as spaces or non-ASCII letters, make it fail. For a volunteer's website field, validate, then check `parse_url($url, PHP_URL_SCHEME)` against `http` and `https`.
code
php · 16 lines<?php
declare(strict_types=1);
$email = trim((string) ($_POST['email'] ?? ''));
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$errors['email'] = 'Enter a valid email address.';
}
$site = trim((string) ($_POST['website'] ?? ''));
if ($site !== '') {
$scheme = strtolower((string) parse_url($site, PHP_URL_SCHEME));
$isWeb = match ($scheme) { 'http', 'https' => true, default => false };
if (filter_var($site, FILTER_VALIDATE_URL) === false || !$isWeb) {
$errors['website'] = 'Enter an http or https address.';
}
}go deeper
Recall that both filters check format only, return false on failure, and that an email address is confirmed by sending a message to it.
Explain the email filter's dotted-domain, length and Unicode rules and the URL filter's scheme and host rules, including which schemes pass without a host.
Show that you allow-list schemes after FILTER_VALIDATE_URL, understand why javascript: and file: URLs pass, and keep validation separate from escaping and fetch protection.
Decide which guarantees the sign-up flow needs, syntax, ownership or reachability, and which mechanism provides each, instead of expecting one filter to cover all three.
## Both filters check form, not reality A volunteer sign-up form typically asks for an email address and, optionally, a personal or organisation website. PHP's two filters for these fields answer the same narrow question: *is this string shaped like an email address, or like a URL?* Neither contacts a mail server or fetches a page, and neither knows what your application will do with the value. ## FILTER_VALIDATE_EMAIL The email filter runs the value through a fixed regular expression. The practical rules: - The whole address may be at most **320 characters**. - The local part (before `@`) may contain letters, digits and the usual punctuation, or a quoted string. Non-ASCII letters in the local part are accepted **only** with `FILTER_FLAG_EMAIL_UNICODE`. - The domain must be ASCII labels, optionally in `xn--` punycode form, with **at least one dot**. `user@localhost` and other dotless domains fail, as do internationalised domain names written in Unicode. - An address in square brackets (IPv4 or `IPv6:`) is accepted as a domain. - Surrounding whitespace is **not** trimmed; `" [email protected]"` fails, so trim deliberately before validating. What it cannot tell you: 1. whether the domain has mail servers; 2. whether the mailbox exists; 3. whether the person submitting it owns it. For a sign-up flow the only real proof is a confirmation message with a link or code. The filter's job is to catch typos and obvious junk before that step. ## FILTER_VALIDATE_URL The URL filter first checks that the string contains only characters valid in a URL: if removing invalid characters would change it, validation fails. So spaces, non-ASCII letters (including internationalised domains in Unicode) and many control characters are rejected. It then parses the URL and applies these checks: | Case | Requirement | |---|---| | any URL | a scheme must be present | | `http` or `https` | a host is required and must be a valid hostname or bracketed IPv6 | | `mailto`, `news`, `file` | may have no host | | any other scheme | needs *a* host, which is not validated as a hostname | | `FILTER_FLAG_PATH_REQUIRED` | a path must be present | | `FILTER_FLAG_QUERY_REQUIRED` | a query string must be present | The consequence is that "valid URL" is much broader than "web address": - `file:///etc/passwd` is valid; - `ftp://example.com/` is valid; - `javascript://comment%0Aalert(1)` is valid, because a non-web scheme only needs some host, and a browser treats that string as script when it is used as a link. ## Closing the gap with an allow-list For a website field the rule you actually want is "an http or https URL". Enforce it after the filter: 1. `filter_var($url, FILTER_VALIDATE_URL)` must not return `false`. 2. `strtolower((string) parse_url($url, PHP_URL_SCHEME))` must be `http` or `https`. 3. Optionally apply length limits, and reject hosts you do not want linked. The same idea applies to email: if the organisation only accepts addresses on certain domains, check the part after the last `@` against that list after validation succeeds. ## What remains the job of other layers Validation decides whether to accept the value. It does not make the value safe for every use: - a valid URL written into an `href` still needs attribute escaping; - a valid email address still needs a parameterised query when stored; - a server that later *fetches* a user-supplied URL needs protection against requests to internal addresses, which no syntax check provides. ## Normalise before you validate Neither filter trims or case-folds, so decide on normalisation explicitly: - **Trim** surrounding whitespace, which users paste in from other applications. - **Lowercase the email domain** if you compare addresses for duplicates; the local part is technically case-sensitive, so many systems lowercase only the domain, while others lowercase the whole address as a product decision. - **Store the normalised form** and validate that form, so the value that passed the check is the value that is saved. Normalisation that happens after validation can reintroduce values the check would have rejected. ## Summary for the interview The strong answer names the concrete gaps: the email filter's dotted-domain and ASCII rules and its silence on deliverability, and the URL filter's acceptance of any scheme. Then it names the fix: validate, then allow-list the scheme, then confirm ownership where it matters.
- Why does filter_var('info@localhost', FILTER_VALIDATE_EMAIL) return false?The email pattern requires a domain with at least one dot, following the rule that only fully qualified domains are used in mail routing. Dotless hosts such as `localhost` or an internal hostname fail. Test fixtures therefore need addresses like `[email protected]`.
- Does FILTER_FLAG_EMAIL_UNICODE make internationalised domains valid?No. The flag only widens the local part before `@` to Unicode letters and digits. The domain must still be ASCII labels, with internationalised domains in their `xn--` punycode form, so convert a Unicode domain before validating if you need to accept one.
- Is a URL that passes FILTER_VALIDATE_URL safe to fetch from the server?No. The filter checks syntax only; `http://127.0.0.1/` and other internal addresses are perfectly valid. Before fetching user-supplied URLs you need an allow-list of schemes and hosts and a check of the resolved address, which is a separate server-side request forgery defence.
saying these in an interview costs you the question
- FILTER_VALIDATE_EMAIL confirms the mailbox exists
- FILTER_VALIDATE_URL only accepts http and https URLs
- A URL that passes FILTER_VALIDATE_URL is safe to put in a link
- FILTER_FLAG_EMAIL_UNICODE allows Unicode domain names
- FILTER_VALIDATE_EMAIL trims spaces around the address