diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -69,4 +69,4 @@
 - [ ] Encode name and labels on register.
 - [x] Implement ReaderT for Concurrent Registry.
 - [x] Library documentation and example.
-- [ ] [Name and label validation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels)
+- [x] [Name and label validation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels)
diff --git a/prometheus.cabal b/prometheus.cabal
--- a/prometheus.cabal
+++ b/prometheus.cabal
@@ -1,5 +1,5 @@
 name:                prometheus
-version:             2.2.2
+version:             2.2.3
 synopsis:            Prometheus Haskell Client
 homepage:            http://github.com/bitnomial/prometheus
 bug-reports:         http://github.com/bitnomial/prometheus/issues
@@ -84,7 +84,7 @@
 
   build-depends: base            >= 4.9  && < 5
                , atomic-primops  >= 0.8  && < 0.9
-               , bytestring      >= 0.10 && < 0.11
+               , bytestring      >= 0.10 && < 0.12
                , containers      >= 0.5  && < 0.7
                , http-client     >= 0.4  && < 0.8
                , http-client-tls >= 0.3  && < 0.4
diff --git a/src/System/Metrics/Prometheus/MetricId.hs b/src/System/Metrics/Prometheus/MetricId.hs
--- a/src/System/Metrics/Prometheus/MetricId.hs
+++ b/src/System/Metrics/Prometheus/MetricId.hs
@@ -1,17 +1,25 @@
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
 module System.Metrics.Prometheus.MetricId where
 
+import           Data.Char (isDigit)
+import           Data.Bifunctor (first)
 import           Data.Map       (Map)
 import qualified Data.Map       as Map
 import           Data.Monoid    (Monoid)
 import           Data.Semigroup (Semigroup)
-import           Data.String    (IsString)
+import           Data.String    (IsString(..))
 import           Data.Text      (Text)
+import qualified Data.Text as Text
 import           Prelude        hiding (null)
 
+-- | Construct with 'makeName' to ensure that names use only valid characters
+newtype Name = Name { unName :: Text } deriving (Show, Eq, Ord, Monoid, Semigroup)
 
-newtype Name = Name { unName :: Text } deriving (Show, Eq, Ord, IsString, Monoid, Semigroup)
+instance IsString Name where
+  fromString = makeName . Text.pack
+
 newtype Labels = Labels { unLabels :: Map Text Text } deriving (Show, Eq, Ord, Monoid, Semigroup)
 
 
@@ -23,11 +31,11 @@
 
 
 addLabel :: Text -> Text -> Labels -> Labels
-addLabel key val = Labels . Map.insert key val . unLabels
+addLabel key val = Labels . Map.insert (makeValid key) val . unLabels
 
 
 fromList :: [(Text, Text)] -> Labels
-fromList = Labels . Map.fromList
+fromList = Labels . Map.fromList . map (first makeValid)
 
 
 toList :: Labels -> [(Text, Text)]
@@ -36,3 +44,22 @@
 
 null :: Labels -> Bool
 null = Map.null . unLabels
+
+
+-- | Make the input match the regex @[a-zA-Z_][a-zA-Z0-9_]@ which
+-- defines valid metric and label names, according to
+-- <https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels>
+-- Replace invalid characters with @_@ and add a leading @_@ if the
+-- first character is only valid as a later character.
+makeValid :: Text -> Text
+makeValid "" = "_"
+makeValid txt = prefix_ <> Text.map (\c -> if allowedChar c then c else '_' ) txt
+  where
+    prefix_ = if isDigit (Text.head txt) then "_" else ""
+    allowedChar :: Char -> Bool
+    allowedChar c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || isDigit c || c == '_'
+
+
+-- | Construct a 'Name', replacing disallowed characters.
+makeName :: Text -> Name
+makeName = Name . makeValid
