diff --git a/lib/sanitize.js b/lib/sanitize.js
index dd02b5d22324f62862bb8a29016c6287531f5789..78d905bb284d379db14d3b1a50bc095c20c3b3b4 100644
--- a/lib/sanitize.js
+++ b/lib/sanitize.js
@@ -28,9 +28,17 @@ module.exports = function (value) {
       throw "not a valid domain";      
     }
   };
-  
+
+  var isOrigin = function() {
+    // allow single hostnames, e.g. localhost
+    if (!value.match(/^https?:\/\/[a-z\d-]+(\.[a-z\d-]+)*(:\d+)?$/i)) {
+      throw "not a valid origin";      
+    }
+  };
+
   return {
     isEmail: isEmail,
-    isDomain: isDomain
+    isDomain: isDomain,
+    isOrigin: isOrigin
   };
 };
diff --git a/lib/validate.js b/lib/validate.js
index ebe808dd35e85057f200f33cdc28900a716ffc45..7eb2a6f04e1444db5875d6fe1aeb1976e98d91be 100644
--- a/lib/validate.js
+++ b/lib/validate.js
@@ -31,11 +31,14 @@ module.exports = function (params) {
           throw k;
         }
       });
-      next();
     } catch(e) {
       var msg = "missing '" + e + "' argument";
-      logger.warn("bad request recieved: " + msg);
+      logger.warn("bad request received: " + msg);
       return httputils.badRequest(resp, msg);
     }
+
+    // this is called outside the try/catch because errors
+    // in the handling of the request should be caught separately
+    next();
   };
 };
diff --git a/lib/wsapi/stage_email.js b/lib/wsapi/stage_email.js
index 7d59d4924ae4e0d1f85df9add857454337e083aa..76bca3b86b2e20b4268e2c53791db39c36228874 100644
--- a/lib/wsapi/stage_email.js
+++ b/lib/wsapi/stage_email.js
@@ -26,7 +26,7 @@ exports.process = function(req, res) {
   // validate
   // should do this one but it's failing for some reason
   sanitize(req.body.email).isEmail();
-  sanitize(req.body.site).isDomain();
+  sanitize(req.body.site).isOrigin();
   
   db.lastStaged(req.body.email, function (err, last) {
     if (err) return wsapi.databaseDown(res, err);
diff --git a/lib/wsapi/stage_user.js b/lib/wsapi/stage_user.js
index 7ff035f29d6f251fece35d7e303dea52b4093a6a..ca1a3318c4eb194829e2a36fce08c628835be335 100644
--- a/lib/wsapi/stage_user.js
+++ b/lib/wsapi/stage_user.js
@@ -30,7 +30,7 @@ exports.process = function(req, resp) {
 
   // validate
   sanitize(req.body.email).isEmail();
-  sanitize(req.body.site).isDomain();
+  sanitize(req.body.site).isOrigin();
 
   db.lastStaged(req.body.email, function (err, last) {
     if (err) return wsapi.databaseDown(resp, err);
diff --git a/tests/add-email-with-assertion-test.js b/tests/add-email-with-assertion-test.js
index ce85baeabde4cf334338b44641b073885cfc091a..aca86c04c874d112f634a5a0b13d8e1bbb992dbb 100755
--- a/tests/add-email-with-assertion-test.js
+++ b/tests/add-email-with-assertion-test.js
@@ -112,7 +112,7 @@ suite.addBatch({
   "stage an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_FIRST_ACCT,
-      site:'fakesite.com'
+      site:'http://fakesite.com:652'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/cert-emails-test.js b/tests/cert-emails-test.js
index d58dea80a63c7fe6f2e1e16a95168febe59f5e38..e8b2ef5a39ad5cc4f6cb9b2851160b47b13dec3b 100755
--- a/tests/cert-emails-test.js
+++ b/tests/cert-emails-test.js
@@ -35,7 +35,7 @@ suite.addBatch({
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'syncer@somehost.com',
       pubkey: 'fakekey',
-      site:'fakesite.com'
+      site:'http://fakesite.com'
     }),
     "succeeds": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/email-throttling-test.js b/tests/email-throttling-test.js
index ba807a7829aa692ca705946e767ec9c461a7a7de..4cceed04d5c83ee9c07d7a4e9bd3d87586d5207a 100755
--- a/tests/email-throttling-test.js
+++ b/tests/email-throttling-test.js
@@ -24,7 +24,7 @@ suite.addBatch({
   "staging a registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'https://fakesite.com:443'
     }),
     "returns 200": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -49,7 +49,7 @@ suite.addBatch({
   "immediately staging another": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'http://fakesite.com:80'
     }),
     "is throttled": function(err, r) {
       assert.strictEqual(r.code, 429);
@@ -74,7 +74,7 @@ suite.addBatch({
   "add a new email address to our account": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: 'second@fakeemail.com',
-      site:'fakesite.com'
+      site:'https://fakesite.com'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -99,7 +99,7 @@ suite.addBatch({
   "re-adding that same new email address a second time": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: 'second@fakeemail.com',
-      site:'fakesite.com'
+      site:'http://fakesite.com'
     }),
     "is throttled with a 429": function(err, r) {
       assert.strictEqual(r.code, 429);
diff --git a/tests/forgotten-email-test.js b/tests/forgotten-email-test.js
index ed0fbc8ce9801f0e03ce04a9cd88bac833e5425e..7c4f875e6dd9de6991f6856226e153ae0158fc28 100755
--- a/tests/forgotten-email-test.js
+++ b/tests/forgotten-email-test.js
@@ -25,7 +25,7 @@ suite.addBatch({
   "staging an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'http://localhost:123'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -74,7 +74,7 @@ suite.addBatch({
   "add a new email address to our account": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: 'second@fakeemail.com',
-      site:'fakesite.com'
+      site:'https://fakesite.foobar.bizbaz.uk'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -137,7 +137,7 @@ suite.addBatch({
   "re-stage first account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'otherfakesite.com'
+      site:'https://otherfakesite.com'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/list-emails-wsapi-test.js b/tests/list-emails-wsapi-test.js
index 7ed5a742a3e6b88e53355479a36e8246e097dbc8..09fa35775cd57cbfc53ae6fa6c475dc0839e7bce 100755
--- a/tests/list-emails-wsapi-test.js
+++ b/tests/list-emails-wsapi-test.js
@@ -27,7 +27,7 @@ suite.addBatch({
   "stage an account": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'syncer@somehost.com',
-      site:'fakesite.com'
+      site:'https://foobar.fakesite.com'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/no-cookie-test.js b/tests/no-cookie-test.js
index 150d92cd448d862b228f2736abb5f86c9bec703d..33a2db02f237d4a816503b19be3cdfbf01e611f9 100755
--- a/tests/no-cookie-test.js
+++ b/tests/no-cookie-test.js
@@ -27,7 +27,7 @@ suite.addBatch({
   "start registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'http://fakesite.com:123'
     }),
     "returns 200": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/password-bcrypt-update-test.js b/tests/password-bcrypt-update-test.js
index c403d6ef715d1250b6671128abd9a64b1b7af923..de9ea66d47d55757750bd0ce29e1d4b29df50c5d 100755
--- a/tests/password-bcrypt-update-test.js
+++ b/tests/password-bcrypt-update-test.js
@@ -46,7 +46,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
-      site:'fakesite.com'
+      site:'https://fakesite.com'
     }),
     "works":     function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/password-length-test.js b/tests/password-length-test.js
index 2c956b1a1313a8a46f94718b0860364b6314f29f..8988e94cda546001dd6eb40b8f54bd57f609e3a5 100755
--- a/tests/password-length-test.js
+++ b/tests/password-length-test.js
@@ -44,7 +44,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'https://fakesite.com:123'
     }),
     "works":     function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/password-update-test.js b/tests/password-update-test.js
index 9d484f56f47025c365bedee2af4a39fac4caf7eb..dcaeda0d5abb553ecd500bfce2d58bd9fd2de44e 100755
--- a/tests/password-update-test.js
+++ b/tests/password-update-test.js
@@ -34,7 +34,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
-      site: 'fakesite.com'
+      site: 'https://fakesite.com:123'
     }),
     "works":     function(err, r) {
       assert.equal(r.code, 200);
diff --git a/tests/primary-then-secondary-test.js b/tests/primary-then-secondary-test.js
index 6b47385a950056d788ba6c144def02c805cacb05..da0e7e5103429ac5533d5b07b91957612752d6fc 100755
--- a/tests/primary-then-secondary-test.js
+++ b/tests/primary-then-secondary-test.js
@@ -81,7 +81,7 @@ suite.addBatch({
   "add a new email address to our account": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: SECONDARY_EMAIL,
-      site:'fakesite.com'
+      site:'https://fakesite.com'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -146,7 +146,7 @@ suite.addBatch({
   "add a new email address to our account": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: SECOND_SECONDARY_EMAIL,
-      site:'fakesite.com'
+      site:'http://fakesite.com:123'
     }),
     "works": function(err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/registration-status-wsapi-test.js b/tests/registration-status-wsapi-test.js
index 394383c207dabeb1b78e1a924d4f91ec55064c37..cac3a6050e4c430cc24da3c71f399fd717e8dddc 100755
--- a/tests/registration-status-wsapi-test.js
+++ b/tests/registration-status-wsapi-test.js
@@ -47,7 +47,7 @@ suite.addBatch({
   "start registration": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'fakesite.com'
+      site:'https://fakesite.com'
     }),
     "returns 200": function(err, r) {
       assert.strictEqual(r.code, 200);
@@ -166,7 +166,7 @@ suite.addBatch({
   "re-registering an existing email": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'first@fakeemail.com',
-      site:'secondfakesite.com'
+      site:'http://secondfakesite.com'
     }),
     "yields a HTTP 200": function (err, r) {
       assert.strictEqual(r.code, 200);
diff --git a/tests/stalled-mysql-test.js b/tests/stalled-mysql-test.js
index cd40adba4c379ee97fa585626ba816ff46eb6cd7..a5bba95070166f2f6691c18a705b6f6e2b5210d8 100755
--- a/tests/stalled-mysql-test.js
+++ b/tests/stalled-mysql-test.js
@@ -146,7 +146,7 @@ suite.addBatch({
   "stage_user": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: 'bogus@bogus.edu',
-      site: 'whatev.er'
+      site: 'https://whatev.er'
     }),
     "fails with 503": function(err, r) {
       assert.strictEqual(r.code, 503);
@@ -175,7 +175,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: "stalltest@whatev.er",
-      site: 'fakesite.com'
+      site: 'http://fakesite.com'
     }),
     "works":     function(err, r) {
       assert.equal(r.code, 200);
@@ -264,7 +264,7 @@ suite.addBatch({
   "stage_email": {
     topic: wsapi.post('/wsapi/stage_email', {
       email: "test2@whatev.er",
-      site: "foo.com"
+      site: "https://foo.com"
     }),
     "fails with 503": function(err, r) {
       assert.strictEqual(r.code, 503);
diff --git a/tests/verifier-test.js b/tests/verifier-test.js
index b2a455dc04807502630199c49333d5a9a42e7572..d9b0589b345916a92708303fc83f57e2007e4826 100755
--- a/tests/verifier-test.js
+++ b/tests/verifier-test.js
@@ -41,7 +41,7 @@ suite.addBatch({
   "account staging": {
     topic: wsapi.post('/wsapi/stage_user', {
       email: TEST_EMAIL,
-      site: TEST_DOMAIN
+      site: TEST_ORIGIN
     }),
     "works":     function(err, r) {
       assert.equal(r.code, 200);